1. 概述
Arquillian 是一个与容器无关的 Jakarta EE 集成测试框架。使用 Arquillian 能极大减轻容器管理、应用部署、框架初始化等繁琐工作。
我们可以专注于编写真正的测试逻辑,而不是折腾测试环境的启动配置。
2. 核心概念
2.1. 部署归档
在容器内部运行测试时,Arquillian 提供了便捷方案:
- ShrinkWrap 类提供 API 用于创建可部署的
*.jar
、*.war
和*.ear
文件 - 通过
@Deployment
注解配置测试部署,该注解标注的方法需返回 ShrinkWrap 对象
2.2. 容器类型
Arquillian 区分三种容器类型:
✅ 远程容器 (Remote)
通过 JMX 等远程协议测试
✅ 托管容器 (Managed)
生命周期由 Arquillian 管理的远程容器
✅ 嵌入式容器 (Embedded)
使用本地协议执行测试的本地容器
按能力维度分类:
- Jakarta EE 应用服务器(如 Glassfish、JBoss)
- Servlet 容器(如 Tomcat、Jetty)
- 独立容器
- OSGI 容器
Arquillian 会自动检测运行时类路径并选择可用容器。
2.3. 测试增强
Arquillian 通过依赖注入等功能增强测试能力:
- 使用
@Inject
注入依赖 - 使用
@Resource
注入资源 - 使用
@EJB
注入 EJB 会话 Bean
2.4. 多部署测试
通过注解创建多个部署:
@Deployment(name="myname" order = 1)
name
:部署文件名称order
:部署执行顺序
使用 @OperateOnDeployment
在指定部署上运行测试:
@Test @OperateOnDeployment("myname")
测试将在名为 myname
的部署容器上执行。
2.5. Arquillian 扩展
当核心功能无法满足测试需求时,Arquillian 提供多种扩展:
- 持久化扩展
- 事务扩展
- 客户端/服务端扩展
- REST 扩展
通过 Maven/Gradle 添加依赖即可启用扩展,常用扩展包括:
- Drone
- Graphene
- Selenium
3. Maven 依赖配置
在 pom.xml
中添加以下依赖:
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>1.1.13.Final</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.glassfish.main.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>4.1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-glassfish-embedded-3.1</artifactId>
<version>1.0.0.Final</version>
<scope>test</scope>
</dependency>
最新版本可在此处查找:arquillian-bom, glassfish-embedded-all, arquillian-glassfish-embedded-3.1。
4. 简单测试示例
4.1. 创建组件
先创建一个简单组件(省略复杂逻辑以便聚焦测试):
public class Component {
public void sendMessage(PrintStream to, String msg) {
to.println(message(msg));
}
public String message(String msg) {
return "Message, " + msg;
}
}
我们将测试该类作为 CDI Bean 时的行为。
4.2. 编写首个 Arquillian 测试
首先指定测试运行器:
@RunWith(Arquillian.class)
使用 @Deployment
注解创建测试部署:
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create(JavaArchive.class)
.addClass(Component.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
关键点:
ShrinkWrap
创建名为test.war
的模拟 Web 归档addClass()
指定测试所需类addAsManifestResource
添加空的 beans.xml 启用 CDI
注入组件并编写测试:
@Inject
private Component component;
@Test
public void testComponent() {
assertEquals("Message, MESSAGE", component.message("MESSAGE"));
component.sendMessage(System.out, "MESSAGE");
}
5. 测试企业级 Java Bean
5.1. 创建 EJB 组件
创建字符串转换工具类:
public class ConvertToLowerCase {
public String convert(String word){
return word.toLowerCase();
}
}
构建无状态会话 Bean:
@Stateless
public class CapsConvertor {
public ConvertToLowerCase getLowerCase(){
return new ConvertToLowerCase();
}
}
创建服务 Bean 并注入依赖:
@Stateless
public class CapsService {
@Inject
private CapsConvertor capsConvertor;
public String getConvertedCaps(final String word){
return capsConvertor.getLowerCase().convert(word);
}
}
5.2. 测试 EJB
使用 Arquillian 测试 EJB 依赖注入:
@Inject
private CapsService capsService;
@Test
public void givenWord_WhenUppercase_ThenLowercase(){
assertTrue("capitalize".equals(capsService.getConvertedCaps("CAPITALIZE")));
assertEquals("capitalize", capsService.getConvertedCaps("CAPITALIZE"));
}
确保所有类正确装配:
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create(JavaArchive.class)
.addClasses(CapsService.class, CapsConvertor.class, ConvertToLowerCase.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
6. 测试 JPA 持久化
6.1. 持久化组件
创建 JPA 实体:
@Entity
public class Car {
@Id
@GeneratedValue
private Long id;
@NotNull
private String name;
// getters and setters
}
创建 EJB 执行数据库操作:
@Stateless
public class CarEJB {
@PersistenceContext(unitName = "defaultPersistenceUnit")
private EntityManager em;
public Car saveCar(Car car) {
em.persist(car);
return car;
}
public List<Car> findAllCars() {
Query query = em.createQuery("SELECT b FROM Car b ORDER BY b.name ASC");
List<Car> entries = query.getResultList();
return entries == null ? new ArrayList<>() : entries;
}
public void deleteCar(Car car) {
car = em.merge(car);
em.remove(car);
}
}
6.2. 持久化测试
配置 ShrinkWrap 包含持久化资源:
.addClasses(Car.class, CarEJB.class)
.addAsResource("META-INF/persistence.xml")
编写测试验证 CRUD 操作:
@Test
public void testCars() {
assertTrue(carEJB.findAllCars().isEmpty());
Car c1 = new Car();
c1.setName("Impala");
Car c2 = new Car();
c2.setName("Lincoln");
carEJB.saveCar(c1);
carEJB.saveCar(c2);
assertEquals(2, carEJB.findAllCars().size());
carEJB.deleteCar(c1);
assertEquals(1, carEJB.findAllCars().size());
}
测试流程:
- 验证初始数据库为空
- 添加两条记录
- 验证记录总数
- 删除一条记录
- 再次验证记录总数
8. 总结
本教程涵盖内容:
✅ Arquillian 核心概念解析
✅ 组件注入测试实践
✅ EJB 集成测试方案
✅ JPA 持久化测试方法
✅ Maven 测试环境搭建
完整代码示例请查阅 GitHub 仓库。