|
| 1 | += Testing WildFly Applications with Arquillian and JUnit 5 |
| 2 | +:summary: Testing applications with WildFly, Arquillian and JUnit 5 |
| 3 | +:includedir: _includes |
| 4 | +include::{includedir}/_attributes.adoc[] |
| 5 | +// you can override any attributes eg to lengthen the |
| 6 | +// time to complete the guide |
| 7 | +:prerequisites-time: 15 |
| 8 | + |
| 9 | +In this guide you will learn how to setup your project for testing with Arquillian and JUnit 5. We will use the |
| 10 | +https://github.com/wildfly-extras/guides/tree/main/arquillian-junit5[arquillian-junt5] example project in this guide. |
| 11 | + |
| 12 | +include::{includedir}/_prerequisites.adoc[] |
| 13 | + |
| 14 | +== Add JUnit and Arquillian Dependencies |
| 15 | + |
| 16 | +In order to use JUnit and Arquillian for your tests, you need to update the Maven `pom.xml`. The best practice is to |
| 17 | +import the Arquillian, JUnit 5 and WildFly Arquillian BOM's. |
| 18 | + |
| 19 | +[source,xml] |
| 20 | +---- |
| 21 | +<dependencyManagement> |
| 22 | + <dependencies> |
| 23 | + <dependency> |
| 24 | + <groupId>jakarta.platform</groupId> |
| 25 | + <artifactId>jakarta.jakartaee-bom</artifactId> |
| 26 | + <version>${version.jakarta.ee}</version> |
| 27 | + <scope>import</scope> |
| 28 | + <type>pom</type> |
| 29 | + </dependency> |
| 30 | + <dependency> |
| 31 | + <groupId>org.jboss.arquillian</groupId> |
| 32 | + <artifactId>arquillian-bom</artifactId> |
| 33 | + <version>${version.org.jboss.arquillian}</version> |
| 34 | + <type>pom</type> |
| 35 | + <scope>import</scope> |
| 36 | + </dependency> |
| 37 | + <dependency> |
| 38 | + <groupId>org.wildfly.arquillian</groupId> |
| 39 | + <artifactId>wildfly-arquillian-bom</artifactId> |
| 40 | + <version>${version.org.wildfly.arquillian}</version> |
| 41 | + <type>pom</type> |
| 42 | + <scope>import</scope> |
| 43 | + </dependency> |
| 44 | + <dependency> |
| 45 | + <groupId>org.junit</groupId> |
| 46 | + <artifactId>junit-bom</artifactId> |
| 47 | + <version>${version.org.junit}</version> |
| 48 | + <type>pom</type> |
| 49 | + <scope>import</scope> |
| 50 | + </dependency> |
| 51 | + </dependencies> |
| 52 | +</dependencyManagement> |
| 53 | +---- |
| 54 | + |
| 55 | +You then need a minimum of the following dependencies. |
| 56 | + |
| 57 | +[source,xml] |
| 58 | +---- |
| 59 | +<dependencies> |
| 60 | + <dependency> |
| 61 | + <groupId>org.junit.jupiter</groupId> |
| 62 | + <artifactId>junit-jupiter</artifactId> |
| 63 | + <scope>test</scope> |
| 64 | + </dependency> |
| 65 | + <dependency> |
| 66 | + <groupId>org.jboss.arquillian.junit5</groupId> |
| 67 | + <artifactId>arquillian-junit5-container</artifactId> |
| 68 | + <scope>test</scope> |
| 69 | + </dependency> |
| 70 | + <dependency> |
| 71 | + <groupId>org.wildfly.arquillian</groupId> |
| 72 | + <artifactId>wildfly-arquillian-container-managed</artifactId> |
| 73 | + <scope>test</scope> |
| 74 | + </dependency> |
| 75 | +</dependencies> |
| 76 | +---- |
| 77 | + |
| 78 | +In this section we will work on writing a test for our application. We will assume here you already have experience |
| 79 | +writing Jakarta EE applications for WildFly. For the purpose of this test, we will use the |
| 80 | +https://docs.wildfly.org/wildfly-maven-plugin[wildfly-maven-plugin] to provision a server for testing. |
| 81 | + |
| 82 | +== Configure POM for Provisioning |
| 83 | + |
| 84 | +[source,xml] |
| 85 | +---- |
| 86 | +<build> |
| 87 | + <plugins> |
| 88 | + <plugin> |
| 89 | + <groupId>org.wildfly.plugins</groupId> |
| 90 | + <artifactId>wildfly-maven-plugin</artifactId> |
| 91 | + <version>${version.wildfly-maven-plugin}</version> |
| 92 | + <configuration> |
| 93 | + <jboss-home>${jboss.home}</jboss-home> |
| 94 | + <provisioning-dir>${jboss.home}</provisioning-dir> |
| 95 | + <feature-packs> |
| 96 | + <feature-pack> |
| 97 | + <groupId>org.wildfly</groupId> |
| 98 | + <artifactId>wildfly-ee-galleon-pack</artifactId> |
| 99 | + </feature-pack> |
| 100 | + </feature-packs> |
| 101 | + <channels> |
| 102 | + <channel> |
| 103 | + <manifest> |
| 104 | + <groupId>org.wildfly.channels</groupId> |
| 105 | + <artifactId>wildfly-ee</artifactId> |
| 106 | + </manifest> |
| 107 | + </channel> |
| 108 | + </channels> |
| 109 | + <layers> |
| 110 | + <layer>ee-core-profile-server</layer> |
| 111 | + <layer>jpa</layer> |
| 112 | + <layer>h2-default-datasource</layer> |
| 113 | + <layer>transactions</layer> |
| 114 | + </layers> |
| 115 | + <galleon-options> |
| 116 | + <jboss-fork-embedded>true</jboss-fork-embedded> |
| 117 | + </galleon-options> |
| 118 | + </configuration> |
| 119 | + <executions> |
| 120 | + <execution> |
| 121 | + <id>provision-server</id> |
| 122 | + <goals> |
| 123 | + <goal>provision</goal> |
| 124 | + </goals> |
| 125 | + <phase>process-test-resources</phase> |
| 126 | + </execution> |
| 127 | + </executions> |
| 128 | + </plugin> |
| 129 | + </plugins> |
| 130 | +</build> |
| 131 | +---- |
| 132 | + |
| 133 | +The above configuration will provision a server based with Jakarta EE Core Profile specifications, Jakarta Persistence, |
| 134 | +Jakarta Transactions and the default H2 data source. The layers can be removed to provision a full WildFly server. |
| 135 | + |
| 136 | +The provisioning is bound to the `process-test-resources` phase. This is the last phase before the `test` phase which |
| 137 | +is when our tests will be executed by default. We need a server before we can use Arquillian for our tests. |
| 138 | + |
| 139 | +== Writing Tests |
| 140 | + |
| 141 | +Now that our POM is configured, we can write a test for our application. The first step is to tell JUnit 5 we want |
| 142 | +to extend the functionality with Arquillian. The simplest approach is to annotate your test with `@ArquillainTest`. The |
| 143 | +other option is to annotate the test with `@ExtendWith(ArquillianExtension.class)`. |
| 144 | + |
| 145 | +[source,java] |
| 146 | +---- |
| 147 | +@ArquillianTest |
| 148 | +public class AddTaskResourceTest { |
| 149 | +} |
| 150 | +---- |
| 151 | + |
| 152 | +=== Client Test |
| 153 | + |
| 154 | +Arquillian can run both in the container or as a client. For the first example we will run as a client. When running as |
| 155 | +a client the test runs outside the container. The simplest way to run as a client is to use the `@RunAsClient` |
| 156 | +annotation. |
| 157 | + |
| 158 | +[source,java] |
| 159 | +---- |
| 160 | +@ArquillianTest |
| 161 | +@RunAsClient |
| 162 | +public class AddTaskResourceTest { |
| 163 | +} |
| 164 | +---- |
| 165 | + |
| 166 | +The next thing Arquillian needs is a deployment. You can use Shrinkwrap to create a deployment. |
| 167 | + |
| 168 | +NOTE: Shrinkwrap is a transitive dependency of Arquillian. |
| 169 | + |
| 170 | +[source,java] |
| 171 | +---- |
| 172 | +@ArquillianTest |
| 173 | +@RunAsClient |
| 174 | +public class AddTaskResourceTest { |
| 175 | +
|
| 176 | + @Deployment |
| 177 | + public static WebArchive createDeployment() { |
| 178 | + return ShrinkWrap.create(WebArchive.class) |
| 179 | + .addPackages(true, "org.wildfly.guide.testing") |
| 180 | + .addAsResource("META-INF/persistence.xml") |
| 181 | + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); |
| 182 | + } |
| 183 | +} |
| 184 | +---- |
| 185 | + |
| 186 | +We can now add a test method using standard JUnit 5 testing strategies. |
| 187 | + |
| 188 | +[source,java] |
| 189 | +---- |
| 190 | +@ArquillianTest |
| 191 | +@RunAsClient |
| 192 | +public class AddTaskResourceTest { |
| 193 | +
|
| 194 | + @ArquillianResource |
| 195 | + private URI uri; |
| 196 | +
|
| 197 | + @Deployment |
| 198 | + public static WebArchive createDeployment() { |
| 199 | + return ShrinkWrap.create(WebArchive.class) |
| 200 | + .addPackages(true, "org.wildfly.guide.testing") |
| 201 | + .addAsResource("META-INF/persistence.xml") |
| 202 | + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); |
| 203 | + } |
| 204 | +
|
| 205 | + @Test |
| 206 | + public void addTask() { |
| 207 | + final Task toAdd = new Task(); |
| 208 | + toAdd.setSummary("This is a test task"); |
| 209 | + toAdd.setDescription("This the test tasks description"); |
| 210 | + try ( |
| 211 | + Client client = ClientBuilder.newClient(); |
| 212 | + Response createdResponse = client.target(UriBuilder.fromUri(uri).path("api/task/")).request() |
| 213 | + .post(Entity.json(toAdd))) { |
| 214 | + Assertions.assertEquals(Response.Status.CREATED, createdResponse.getStatusInfo(), |
| 215 | + () -> String.format("Invalid status: %s", createdResponse.readEntity(String.class))); |
| 216 | + // We should have the location |
| 217 | + try (Response response = client.target(createdResponse.getLocation()).request().get()) { |
| 218 | + Assertions.assertEquals(Response.Status.OK, response.getStatusInfo(), |
| 219 | + () -> String.format("Invalid status: %s - %s", createdResponse.readEntity(String.class), |
| 220 | + createdResponse.getLocation())); |
| 221 | + final Task resolvedTask = response.readEntity(Task.class); |
| 222 | + Assertions.assertNotNull(resolvedTask); |
| 223 | + Assertions.assertTrue(resolvedTask.getId() > 0, |
| 224 | + () -> String.format("Expected the task to have an ID greater than 0: %s", resolvedTask.getId())); |
| 225 | + } |
| 226 | + } |
| 227 | + } |
| 228 | +} |
| 229 | +---- |
| 230 | + |
| 231 | +NOTE: The `@ArquillianResource` can be used in inject various resources from Arquillian and WildFly Arquillian. In this |
| 232 | +example we inject a URI for the deployment. |
| 233 | + |
| 234 | +=== In Container Test |
| 235 | + |
| 236 | +In container tests have a similar structure to client based test. However, the test itself runs inside the container. |
| 237 | +This allows you to use CDI to inject beans into your test for example. |
| 238 | + |
| 239 | +[source,java] |
| 240 | +---- |
| 241 | +@ArquillianTest |
| 242 | +@RequestScoped |
| 243 | +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) |
| 244 | +public class TaskRegistryTest { |
| 245 | +
|
| 246 | + @Inject |
| 247 | + private TaskRegistry taskRegistry; |
| 248 | +
|
| 249 | + @Deployment |
| 250 | + public static WebArchive createDeployment() { |
| 251 | + return ShrinkWrap.create(WebArchive.class) |
| 252 | + // Note for this test we don't use the REST endpoints so we don't need the REST resources |
| 253 | + .addClasses(TaskRegistry.class, |
| 254 | + Priority.class, |
| 255 | + Task.class, |
| 256 | + Producers.class, |
| 257 | + TaskListener.class) |
| 258 | + .addAsResource("META-INF/persistence.xml") |
| 259 | + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); |
| 260 | + } |
| 261 | +
|
| 262 | + @Test |
| 263 | + @Order(1) |
| 264 | + public void addTask(final TestInfo testInfo) { |
| 265 | + final Task task = new Task(); |
| 266 | + task.setAdded(Instant.now()); |
| 267 | + task.setDescription("This is a test task from " + testInfo.getTestMethod() |
| 268 | + .map(Method::getName) |
| 269 | + .orElse("<unknown>")); |
| 270 | + task.setPriority(Priority.IMPORTANT); |
| 271 | + task.setSummary("Test summary"); |
| 272 | + final var addedTask = taskRegistry.add(task); |
| 273 | + Assertions.assertEquals(task, addedTask); |
| 274 | + } |
| 275 | +} |
| 276 | +---- |
| 277 | + |
| 278 | +// Always keep a what's next? section to let the user know what could be achieved next |
| 279 | +== What's next? |
| 280 | + |
| 281 | +Using JUnit 5 and Arquillian for testing offers several options for testing your application with WildFly. WildFly |
| 282 | +Arquillian includes some additional utilities not discussed in this guide such as the ability to configure server |
| 283 | +settings before your test executes. An advanced guide will dig deeper into the additional options for using Arquillian |
| 284 | +on WildFly. |
| 285 | + |
| 286 | +// Always add this section last to link to any relevant content |
| 287 | +[[references]] |
| 288 | +== References |
| 289 | + |
| 290 | +* https://arquillian.org[Arquillian] |
| 291 | +* https://docs.wildfly.org/wildfly-maven-plugin[WildFly Maven Plugin] |
| 292 | +* https://github.com/wildfly-extras/guides/tree/main/arquillian-junit5[Example Project] |
0 commit comments