Arquillian 简介
1. 概述
Arquillian 是 Jakarta EE 的容器无关集成测试框架。使用 Arquillian 可以最大限度地减少管理容器、部署、框架初始化等的负担。
我们可以专注于编写实际测试,而不是引导测试环境。
2. 核心概念
2.1. 部署
在容器内运行时,有一种简单的方法可以测试我们的应用程序。
首先,ShrinkWrap类提供了一个 API 来创建可部署的 *.jar、*.war 和 *.ear文件。
然后,Arquillian 允许我们在返回ShrinkWrap对象的方法上使用*@Deployment*注解配置测试部署。
2.2. 容器
Arquillian 区分了三种不同类型的容器:
- 远程 - 使用 JMX 等远程协议进行测试
- 托管 - 远程容器,但它们的生命周期由 Arquillian 管理
- 嵌入式 - 使用本地协议执行测试的本地容器
此外,我们可以根据容器的功能对容器进行分类:
- 部署在 Glassfish 或 JBoss 等应用服务器上的 Jakarta EE 应用程序
- 部署在 Tomcat 或 Jetty 上的 Servlet 容器
- 独立容器
- OSGI 容器
它检查运行时类路径并自动选择可用的容器。
2.3. 测试丰富
Arquillian 通过提供例如依赖注入来丰富测试,以便我们可以轻松编写测试。
我们可以使用*@Inject注入依赖项,使用@Resource注入资源,使用@EJB*注入EJB 会话 bean等。
2.4. 多个测试
我们可以使用注解创建多个部署:
@Deployment(name="myname" order = 1)
其中 name 是部署文件的名称,order 参数是部署的执行顺序,所以我们现在可以使用注解同时在多个部署上运行测试:
@Test @OperateOnDeployment("myname")
使用*@Deployment注释中定义的顺序在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 、org.glassfish.main.extras 、org.jboss.arquillian.container 。
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;
}
}
使用 Arquillian,我们想测试这个类在作为 CDI bean 调用时的行为是否正确。
4.2. 编写我们的第一个 Arquillian 测试
首先,我们需要指定我们的测试类应该使用特定于框架的运行器来运行:
@RunWith(Arquillian.class)
如果我们要在容器中运行测试,我们需要使用*@Deployment*注解。
Arquillian 不使用整个类路径来隔离测试存档。相反,它使用ShrinkWrap类,这是一个用于创建档案的 Java API。当我们创建要测试的存档时,我们指定要在类路径中包含哪些文件以使用测试。在部署期间,ShrinkWrap仅隔离测试所需的类。
使用*addclass()*方法,我们可以指定所有必要的类,还可以添加一个空的清单资源。
JavaArchive.class创建一个名为 test.war 的模型 Web 存档,该文件被部署到容器中,然后被 Arquillian 用于执行测试:
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create(JavaArchive.class)
.addClass(Component.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
然后我们在测试中注入我们的组件:
@Inject
private Component component;
最后,我们执行我们的测试:
assertEquals("Message, MESSAGE",component.message(("MESSAGE")));
component.sendMessage(System.out, "MESSAGE");
5. 测试 Enterprise Java Bean
5.1. Enterprise Java Bean
使用 Arquillian,我们可以测试 Enterprise Java Bean 的依赖注入,为此我们创建一个类,该类具有将任何单词转换为小写的方法:
public class ConvertToLowerCase {
public String convert(String word){
return word.toLowerCase();
}
}
使用这个类,我们创建一个无状态类来调用之前创建的方法:
@Stateless
public class CapsConvertor {
public ConvertToLowerCase getLowerCase(){
return new ConvertToLowerCase();
}
}
CapsConvertor类被注入到服务 bean 中:
@Stateless
public class CapsService {
@Inject
private CapsConvertor capsConvertor;
public String getConvertedCaps(final String word){
return capsConvertor.getLowerCase().convert(word);
}
}
5.2. 测试Enterprise Java Bean
现在我们可以使用 Arquillian 来测试我们的Enterprise Java Bean,注入CapsService:
@Inject
private CapsService capsService;
@Test
public void givenWord_WhenUppercase_ThenLowercase(){
assertTrue("capitalize".equals(capsService.getConvertedCaps("CAPITALIZE")));
assertEquals("capitalize", capsService.getConvertedCaps("CAPITALIZE"));
}
使用ShrinkWrap,我们确保所有类都正确连接:
@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. 持久性
我们还可以使用 Arquillian 来测试持久性。首先,我们将创建我们的实体:
@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);
}
}
使用saveCar我们可以将汽车名称保存到数据库中,我们可以使用findAllCars 获取存储的所有汽车,还可以使用deleteCar从数据库中删除汽车。
6.2. 使用 Arquillian 测试持久性
现在我们可以使用 Arquillian 执行一些基本测试。
首先,我们将我们的类添加到我们的ShrinkWrap 中:
.addClasses(Car.class, CarEJB.class)
.addAsResource("META-INF/persistence.xml")
然后我们创建我们的测试:
@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());
}
在这个测试中,我们首先创建了四个汽车实例,并检查数据库中的行数是否与我们创建的相同。