使用 Arquillian 在 Servlet 容器中测试 Jakarta EE 9 Web 应用程序

2022-11-23   出处: ITNEXT  作/译者:Hantsy/Yilia


  在本文中,我们将讨论如何使用 Arquillian 测试框架在 Servlet 容器中测试这些组件。Arquillian 项目官方支持 Apache Tomcat 和 Eclipse Jetty,更多信息请访问Arquillian Container TomcatArquillian Container Jetty。目前,这两个项目都提供了一个嵌入式容器适配器,支持最新的 Apache Tomcat 10 和 Eclipse Jetty 11,但没有可用的托管和远程适配器。如果你是 Arquillian 新手,请先阅读官方入门指南,以了解 Arquillian 的基本知识。

配置 Arquillian

首先将 Arquillian Core 和 JUnit BOM添加到项目pom.xml文件的dependencyManagement 部分。

  1. <dependencyManagement>
  2. // ...
  3. <dependencies>
  4. <dependency>
  5. <groupId>org.jboss.arquillian</groupId>
  6. <artifactId>arquillian-bom</artifactId>
  7. <version>${arquillian-bom.version}</version>
  8. <scope>import</scope>
  9. <type>pom</type>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.junit</groupId>
  13. <artifactId>junit-bom</artifactId>
  14. <version>${junit-jupiter.version}</version>
  15. <type>pom</type>
  16. <scope>import</scope>
  17. </dependency>
  18. </dependencies>
  19. </dependencyManagement>

在项目dependencies 部分添加以下依赖项。

  1. <dependencies>
  2. //...
  3. <dependency>
  4. <groupId>org.jboss.arquillian.protocol</groupId>
  5. <artifactId>arquillian-protocol-servlet-jakarta</artifactId>
  6. <scope>test</scope>
  7. </dependency>
  8. <dependency>
  9. <groupId>org.jboss.shrinkwrap.resolver</groupId>
  10. <artifactId>shrinkwrap-resolver-impl-maven</artifactId>
  11. <scope>test</scope>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.junit.jupiter</groupId>
  15. <artifactId>junit-jupiter</artifactId>
  16. <scope>test</scope>
  17. </dependency>
  18. </dependencies>

还可以添加以下测试utility libs来改进测试代码。

  1. <dependencies>
  2. //....
  3. <dependency>
  4. <groupId>org.junit.jupiter</groupId>
  5. <artifactId>junit-jupiter-params</artifactId>
  6. <scope>test</scope>
  7. </dependency>
  8. <dependency>
  9. <groupId>org.mockito</groupId>
  10. <artifactId>mockito-core</artifactId>
  11. <scope>test</scope>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.hamcrest</groupId>
  15. <artifactId>hamcrest</artifactId>
  16. <scope>test</scope>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.assertj</groupId>
  20. <artifactId>assertj-core</artifactId>
  21. <scope>test</scope>
  22. </dependency>
  23. </dependencies>

接下来,我们将配置 Arquillian Tomcat 嵌入式适配器以针对嵌入式 Apache Tomcat 容器运行测试代码。

配置 Arquillian Tomcat 嵌入式适配器

创建一个新的 Maven 配置文件以集中 Arquillian tomcat embedded adapter.的所有配置。

  1. <profile>
  2. <id>arq-tomcat-embedded</id>
  3. <properties>
  4. <skipTests>false</skipTests>
  5. </properties>
  6. <dependencies>
  7. <dependency>
  8. <groupId>org.jboss.arquillian.container</groupId>
  9. <artifactId>arquillian-tomcat-embedded-10</artifactId>
  10. <version>${arquillian-tomcat.version}</version>
  11. <scope>test</scope>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.apache.tomcat.embed</groupId>
  15. <artifactId>tomcat-embed-core</artifactId>
  16. <version>${tomcat.version}</version>
  17. <scope>test</scope>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.apache.tomcat.embed</groupId>
  21. <artifactId>tomcat-embed-jasper</artifactId>
  22. <version>${tomcat.version}</version>
  23. <scope>test</scope>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.apache.tomcat.embed</groupId>
  27. <artifactId>tomcat-embed-websocket</artifactId>
  28. <version>${tomcat.version}</version>
  29. <scope>test</scope>
  30. </dependency>
  31. </dependencies>
  32. </profile>

测试 Jakarta 组件

对于简单的 POJO,可以编写一个简单的 JUnit 测试来验证功能。例如,GreetingMessage是一个用于组装可读问候消息的简单 POJO。我们可以编写一个简单的 JUnit 测试来检查它是否按预期工作。

  1. public class GreetingMessageTest {
  2. @Test
  3. public void testGreetingMessage() {
  4. var message = GreetingMessage.of("Say Hello to JatartaEE");
  5. assertThat(message.getMessage()).isEqualTo("Say Hello to JatartaEE");
  6. }
  7. }

GreetingService bean 本身只是实现了向目标问候的简单功能,其中目标来自于用buildGreetingMessage方法接收的参数。就像前面的测试示例一样,创建一个简单的 JUnit 测试来验证它是否按预期运行。

  1. public class GreetingServiceUnitTest {
  2. GreetingService service;
  3. @BeforeEach
  4. public void setup() {
  5. service = new GreetingService();
  6. }
  7. @Test
  8. public void testGreeting() {
  9. var message = service.buildGreetingMessage("JakartaEE");
  10. assertThat(message.getMessage()).startsWith("Say Hello to JakartaEE");
  11. }
  12. }

Hellobean 取决于GreetingServicebean 。要在单元测试中测试Hello的功能,我们可以使用 Mockito 来隔离依赖项 - GreetingService. 在下面的HelloTest中,tests里面创建了一个GreetingService的模拟对象

  1. public class HelloTest {
  2. @ParameterizedTest
  3. @MethodSource("provideQueryCriteria")
  4. public void testCreateMessage(String name, String result) {
  5. var service = mock(GreetingService.class);
  6. given(service.buildGreetingMessage(name)).willReturn(GreetingMessage.of("Say Hello to " + name));
  7. var hello = new Hello(service);
  8. hello.setName(name);
  9. hello.createMessage();
  10. assertThat(hello.getName()).isEqualTo(name);
  11. assertThat(hello.getMessage().getMessage()).isEqualTo(result);
  12. verify(service, times(1)).buildGreetingMessage(anyString());
  13. verifyNoMoreInteractions(service);
  14. }
  15. static Stream<Arguments> provideQueryCriteria() {
  16. return Stream.of(
  17. Arguments.of("Tomcat", "Say Hello to Tomcat"),
  18. Arguments.of("JakartaEE", "Say Hello to JakartaEE")
  19. );
  20. }
  21. }

  我们已经在单元测试中测试了简单的 POJO,对于其他 Jakarta EE 组件,例如 Servlet、Jakarta Pages 等,必须在 Servlet 容器中验证功能,要使用 Arquillian 为这种场景编写集成测试。
  为了在不同的阶段运行单元测试和集成测试,我们可以像下面的示例一样,配置maven-surefire-plugin以及maven-failsafe-plugin,以确保集成测试用例在integration-test阶段运行。

  1. <plugins>
  2. //...
  3. <plugin>
  4. <groupId>org.apache.maven.plugins</groupId>
  5. <artifactId>maven-surefire-plugin</artifactId>
  6. <version>${maven-surefire-plugin.version}</version>
  7. <configuration>
  8. <skipTests>${skipTests}</skipTests>
  9. </configuration>
  10. <executions>
  11. <execution>
  12. <id>default-test</id>
  13. <phase>test</phase>
  14. <goals>
  15. <goal>test</goal>
  16. </goals>
  17. <configuration>
  18. <excludes>
  19. <exclude>/it/</exclude>
  20. </excludes>
  21. </configuration>
  22. </execution>
  23. </executions>
  24. </plugin>
  25. <plugin>
  26. <groupId>org.apache.maven.plugins</groupId>
  27. <artifactId>maven-failsafe-plugin</artifactId>
  28. <version>${maven-failsafe-plugin.version}</version>
  29. <configuration>
  30. <skipITs>${skipTests}</skipITs>
  31. </configuration>
  32. <executions>
  33. <execution>
  34. <id>integration-test</id>
  35. <phase>integration-test</phase>
  36. <goals>
  37. <goal>integration-test</goal>
  38. <goal>verify</goal>
  39. </goals>
  40. <configuration>
  41. <includes>
  42. <include>/it/</include>
  43. </includes>
  44. </configuration>
  45. </execution>
  46. </executions>
  47. </plugin>
  48. </plugins>

首先,让我们看一下用于测试简单 CDI bean GreetingService`GreetingServiceTest

  1. @ExtendWith(ArquillianExtension.class)
  2. public class GreetingServiceTest {
  3. private final static Logger LOGGER = Logger.getLogger(GreetingServiceTest.class.getName());
  4. @Deployment
  5. public static WebArchive createDeployment() {
  6. var war = ShrinkWrap.create(WebArchive.class)
  7. .addClass(GreetingMessage.class)
  8. .addClass(GreetingService.class)
  9. .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
  10. .addAsWebInfResource("test-web.xml", "web.xml")
  11. .addAsWebInfResource(new File("src/main/webapp/WEB-INF/jetty-env.xml"), "jetty-env.xml");
  12. Deployments.addExtraJars(war);
  13. LOGGER.log(Level.INFO, "war deployment: {0}", war.toString(true));
  14. return war;
  15. }
  16. @Inject
  17. GreetingService service;
  18. @Test
  19. @DisplayName("testing buildGreetingMessage")
  20. public void should_create_greeting() {
  21. LOGGER.log(Level.INFO, " Running test:: GreetingServiceTest#should_create_greeting ... ");
  22. var message = service.buildGreetingMessage("Jakarta EE");
  23. assertTrue(message.getMessage().startsWith("Say Hello to Jakarta EE at "),
  24. "message should start with \"Say Hello to Jakarta EE at \"");
  25. }
  26. }

  如你所见,Arquillian 集成测试用 @ExtendWith(ArquillianExtension.class)注释,这是标准的 JUnit 5 扩展。
在 Arquillian 测试中,必须通过静态 @Deployment注解方法创建最小部署存档。在 @Deployment方法中,可以在运行测试用例之前准备要打包并部署到目标运行时的资源。
  在测试类中,可以像CDI bean一样注入可用的bean,比如在 GreetingService这里注入,然后在测试方法中,使用 GreetingServicebean来验证功能。
打开终端,执行以下命令运行 GreetingServiceTest.

  1. mvn clean verify -Parq-tomcat-embeded -Dit.test=GreetingServiceTest

  在运行 Arquillian 测试时,它会将部署资源打包成一个可部署的归档文件,并将其部署到目标容器中,然后在容器中运行测试,JUnit 客户端代理将在容器中通过与测试交互的代理收集运行结果。
接下来测试GreetingResource

  1. @ExtendWith(ArquillianExtension.class)
  2. public class GreetingResourceTest {
  3. private final static Logger LOGGER = Logger.getLogger(GreetingResourceTest.class.getName());
  4. @Deployment(testable = false)
  5. public static WebArchive createDeployment() {
  6. var war = ShrinkWrap.create(WebArchive.class)
  7. .addClass(GreetingMessage.class)
  8. .addClass(GreetingService.class)
  9. .addClasses(GreetingResource.class)
  10. .addClasses(RestActivator.class)
  11. // Enable CDI (Optional since Java EE 7.0)
  12. .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
  13. .addAsWebInfResource("test-web.xml", "web.xml")
  14. .addAsWebInfResource(new File("src/main/webapp/WEB-INF/jetty-env.xml"), "jetty-env.xml");
  15. Deployments.addExtraJars(war);
  16. LOGGER.log(Level.INFO, "war deployment: {0}", war.toString(true));
  17. return war;
  18. }
  19. @ArquillianResource
  20. private URL base;
  21. private Client client;
  22. @BeforeEach
  23. public void setup() {
  24. LOGGER.info("call BeforeEach");
  25. this.client = ClientBuilder.newClient();
  26. }
  27. @AfterEach
  28. public void teardown() {
  29. LOGGER.info("call AfterEach");
  30. if (this.client != null) {
  31. this.client.close();
  32. }
  33. }
  34. @Test
  35. @DisplayName("Given a name:`JakartaEE` should return `Say Hello to JakartaEE`")
  36. public void should_create_greeting() throws MalformedURLException {
  37. LOGGER.log(Level.INFO, " client: {0}, baseURL: {1}", new Object[]{client, base});
  38. final var greetingTarget = this.client.target(new URL(this.base, "api/greeting/JakartaEE").toExternalForm());
  39. try (final Response greetingGetResponse = greetingTarget.request()
  40. .accept(MediaType.APPLICATION_JSON)
  41. .get()) {
  42. assertThat(greetingGetResponse.getStatus()).isEqualTo(200);
  43. assertThat(greetingGetResponse.readEntity(GreetingMessage.class).getMessage())
  44. .startsWith("Say Hello to JakartaEE");
  45. }
  46. }
  47. }

GreetingServiceTest不同,为了测试 GreetingResource 的功能,我们使用 Jakarta REST Client API 在客户端视图中与 HTTP API 进行交互。
在注解 @Deployment中添加 testable=false属性,意味着所有测试都将在客户端模式下运行。或者,也可以在测试方法上添加一个 @RunAsClient以在本地运行它。部署后, @ArquillianResource将在容器中注入部署存档的基础 URL。
执行以下命令运行 GreetingServiceTest

  1. mvn clean verify -Parq-tomcat-embeded -Dit.test=GreetingResourceTest

  如果@Deployment(testable=true)应用于部署方法,所有测试都以客户端模式运行,不能像前面的示例那样在测试类中注入 bean。
同样,我们可以创建 client mode 测试来验证简单的 Jakarta Servlet、Jakarta Faces、Jakarta Pages 等的功能。完整的代码可以在这里找到。
要验证呈现的 HTML 页面中的 HTML 元素和 Ajax 交互,请检查Arquillian Extension DroneArquillian Graphene

配置 Jetty 嵌入式适配器

添加用于配置 Arquillian Jetty 嵌入式适配器的新 Maven 配置文件。

  1. <profile>
  2. <id>arq-jetty-embedded</id>
  3. <properties>
  4. <skipTests>false</skipTests>
  5. </properties>
  6. <dependencies>
  7. <dependency>
  8. <groupId>org.jboss.arquillian.container</groupId>
  9. <artifactId>arquillian-jetty-embedded-11</artifactId>
  10. <version>${arquillian-jetty.version}</version>
  11. <scope>test</scope>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.eclipse.jetty</groupId>
  15. <artifactId>jetty-annotations</artifactId>
  16. <scope>test</scope>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.eclipse.jetty</groupId>
  20. <artifactId>jetty-plus</artifactId>
  21. <scope>test</scope>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.eclipse.jetty</groupId>
  25. <artifactId>jetty-deploy</artifactId>
  26. <scope>test</scope>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.eclipse.jetty</groupId>
  30. <artifactId>jetty-servlet</artifactId>
  31. <scope>test</scope>
  32. </dependency>
  33. <dependency>
  34. <groupId>org.eclipse.jetty</groupId>
  35. <artifactId>jetty-webapp</artifactId>
  36. <scope>test</scope>
  37. </dependency>
  38. <dependency>
  39. <groupId>org.eclipse.jetty</groupId>
  40. <artifactId>jetty-cdi</artifactId>
  41. <scope>test</scope>
  42. <!-- to remove when 11.0.10 released -->
  43. <!-- see https://github.com/eclipse/jetty.project/pull/7991 -->
  44. <version>${jetty.version}</version>
  45. </dependency>
  46. <dependency>
  47. <groupId>org.eclipse.jetty.websocket</groupId>
  48. <artifactId>websocket-jakarta-server</artifactId>
  49. <scope>test</scope>
  50. </dependency>
  51. <dependency>
  52. <groupId>org.eclipse.jetty</groupId>
  53. <artifactId>apache-jsp</artifactId>
  54. <scope>test</scope>
  55. </dependency>
  56. <dependency>
  57. <groupId>org.slf4j</groupId>
  58. <artifactId>slf4j-simple</artifactId>
  59. <version>1.7.36</version>
  60. <scope>test</scope>
  61. </dependency>
  62. <!-- see: https://github.com/arquillian/arquillian-container-jetty/pull/108 -->
  63. <!--<dependency>
  64. <groupId>org.jboss.arquillian.testenricher</groupId>
  65. <artifactId>arquillian-testenricher-cdi-jakarta</artifactId>
  66. </dependency>-->
  67. </dependencies>
  68. </profile>

例如,基于嵌入jetty容器的配置再次执行测试:

  1. mvn clean verify -Parq-jetty-embeded -Dit.test=GreetingResourceTest

配置 Arquillian Weld Embedded

Arquillian 项目提供了一个官方扩展来测试Weld 容器中的 CDI bean。
新建Maven配置文件配置Arquillian Weld Embedded Adapter,maven-failsafe-plugin用于过滤Jakarta Servlet、Jakarta Faces等测试。

  1. <profile>
  2. <id>arq-weld</id>
  3. <properties>
  4. <skipTests>false</skipTests>
  5. </properties>
  6. <dependencies>
  7. <dependency>
  8. <groupId>org.jboss.arquillian.container</groupId>
  9. <artifactId>arquillian-weld-embedded</artifactId>
  10. <version>${arquillian-weld-embedded.version}</version>
  11. <scope>test</scope>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.jboss.weld</groupId>
  15. <artifactId>weld-core-impl</artifactId>
  16. <version>${weld.version}</version>
  17. <scope>test</scope>
  18. </dependency>
  19. </dependencies>
  20. <build>
  21. <plugins>
  22. <plugin>
  23. <groupId>org.apache.maven.plugins</groupId>
  24. <artifactId>maven-failsafe-plugin</artifactId>
  25. <version>${maven-failsafe-plugin.version}</version>
  26. <configuration>
  27. <systemPropertyVariables>
  28. <arquillian.launch>arq-weld</arquillian.launch>
  29. </systemPropertyVariables>
  30. <excludes>
  31. <exclude>*/it/GreetingResourceTest</exclude>
  32. <exclude>*/it/GreetingServletTest</exclude>
  33. </excludes>
  34. </configuration>
  35. </plugin>
  36. </plugins>
  37. </build>
  38. </profile>

执行以下命令以运行GreetingServiceTest.

  1. mvn clean verify -Parq-weld -Dit.test=GreetingServiceTest

Jakarta Servlet、Jakarta Pages 和 Jakarta Faces 的测试代码需要一个 Servlet 容器。
完整代码可从github上获取


声明:本文为本站编辑转载,文章版权归原作者所有。文章内容为作者个人观点,本站只提供转载参考(依行业惯例严格标明出处和作译者),目的在于传递更多专业信息,普惠测试相关从业者,开源分享,推动行业交流和进步。 如涉及作品内容、版权和其它问题,请原作者及时与本站联系(QQ:1017718740),我们将第一时间进行处理。本站拥有对此声明的最终解释权!欢迎大家通过新浪微博(@测试窝)或微信公众号(测试窝)关注我们,与我们的编辑和其他窝友交流。
234° /2345 人阅读/0 条评论 发表评论

登录 后发表评论
最新文章