Testing WildFly Applications with Arquillian and JUnit 5

In this guide you will learn how to setup your project for testing with Arquillian and JUnit 5. We will use the arquillian-junt5 example project in this guide.

Prerequisites

To complete this guide, you need:

  • Roughly 15 minutes

  • JDK 17+ installed with JAVA_HOME configured appropriately

  • Apache Maven 3.9+

Add JUnit and Arquillian Dependencies

In order to use JUnit and Arquillian for your tests, you need to update the Maven pom.xml. The best practice is to import the Arquillian, JUnit 5 and WildFly Arquillian BOM’s.

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-bom</artifactId>
            <version>${version.jakarta.ee}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian</groupId>
            <artifactId>arquillian-bom</artifactId>
            <version>${version.org.jboss.arquillian}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.wildfly.arquillian</groupId>
            <artifactId>wildfly-arquillian-bom</artifactId>
            <version>${version.org.wildfly.arquillian}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.junit</groupId>
            <artifactId>junit-bom</artifactId>
            <version>${version.org.junit}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

You then need a minimum of the following dependencies.

<dependencies>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.jboss.arquillian.junit5</groupId>
        <artifactId>arquillian-junit5-container</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.wildfly.arquillian</groupId>
        <artifactId>wildfly-arquillian-container-managed</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

In this section we will work on writing a test for our application. We will assume here you already have experience writing Jakarta EE applications for WildFly. For the purpose of this test, we will use the wildfly-maven-plugin to provision a server for testing.

Configure POM for Provisioning

<build>
    <plugins>
        <plugin>
            <groupId>org.wildfly.plugins</groupId>
            <artifactId>wildfly-maven-plugin</artifactId>
            <version>${version.wildfly-maven-plugin}</version>
            <configuration>
                <jboss-home>${jboss.home}</jboss-home>
                <provisioning-dir>${jboss.home}</provisioning-dir>
                <feature-packs>
                    <feature-pack>
                        <groupId>org.wildfly</groupId>
                        <artifactId>wildfly-ee-galleon-pack</artifactId>
                    </feature-pack>
                </feature-packs>
                <channels>
                    <channel>
                        <manifest>
                            <groupId>org.wildfly.channels</groupId>
                            <artifactId>wildfly-ee</artifactId>
                        </manifest>
                    </channel>
                </channels>
                <layers>
                    <layer>ee-core-profile-server</layer>
                    <layer>jpa</layer>
                    <layer>h2-default-datasource</layer>
                    <layer>transactions</layer>
                </layers>
                <galleon-options>
                    <jboss-fork-embedded>true</jboss-fork-embedded>
                </galleon-options>
            </configuration>
            <executions>
                <execution>
                    <id>provision-server</id>
                    <goals>
                        <goal>provision</goal>
                    </goals>
                    <phase>process-test-resources</phase>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

The above configuration will provision a server based with Jakarta EE Core Profile specifications, Jakarta Persistence, Jakarta Transactions and the default H2 data source. The layers can be removed to provision a full WildFly server.

The provisioning is bound to the process-test-resources phase. This is the last phase before the test phase which is when our tests will be executed by default. We need a server before we can use Arquillian for our tests.

Writing Tests

Now that our POM is configured, we can write a test for our application. The first step is to tell JUnit 5 we want to extend the functionality with Arquillian. The simplest approach is to annotate your test with @ArquillainTest. The other option is to annotate the test with @ExtendWith(ArquillianExtension.class).

@ArquillianTest
public class AddTaskResourceTest {
}

Client Test

Arquillian can run both in the container or as a client. For the first example we will run as a client. When running as a client the test runs outside the container. The simplest way to run as a client is to use the @RunAsClient annotation.

@ArquillianTest
@RunAsClient
public class AddTaskResourceTest {
}

The next thing Arquillian needs is a deployment. You can use Shrinkwrap to create a deployment.

Note
Shrinkwrap is a transitive dependency of Arquillian.
@ArquillianTest
@RunAsClient
public class AddTaskResourceTest {

    @Deployment
    public static WebArchive createDeployment() {
        return ShrinkWrap.create(WebArchive.class)
                .addPackages(true, "org.wildfly.guide.testing")
                .addAsResource("META-INF/persistence.xml")
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    }
}

We can now add a test method using standard JUnit 5 testing strategies.

@ArquillianTest
@RunAsClient
public class AddTaskResourceTest {

    @ArquillianResource
    private URI uri;

    @Deployment
    public static WebArchive createDeployment() {
        return ShrinkWrap.create(WebArchive.class)
                .addPackages(true, "org.wildfly.guide.testing")
                .addAsResource("META-INF/persistence.xml")
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @Test
    public void addTask() {
        final Task toAdd = new Task();
        toAdd.setSummary("This is a test task");
        toAdd.setDescription("This the test tasks description");
        try (
                Client client = ClientBuilder.newClient();
                Response createdResponse = client.target(UriBuilder.fromUri(uri).path("api/task/")).request()
                        .post(Entity.json(toAdd))) {
            Assertions.assertEquals(Response.Status.CREATED, createdResponse.getStatusInfo(),
                    () -> String.format("Invalid status: %s", createdResponse.readEntity(String.class)));
            // We should have the location
            try (Response response = client.target(createdResponse.getLocation()).request().get()) {
                Assertions.assertEquals(Response.Status.OK, response.getStatusInfo(),
                        () -> String.format("Invalid status: %s - %s", createdResponse.readEntity(String.class),
                                createdResponse.getLocation()));
                final Task resolvedTask = response.readEntity(Task.class);
                Assertions.assertNotNull(resolvedTask);
                Assertions.assertTrue(resolvedTask.getId() > 0,
                        () -> String.format("Expected the task to have an ID greater than 0: %s", resolvedTask.getId()));
            }
        }
    }
}
Note
The @ArquillianResource can be used in inject various resources from Arquillian and WildFly Arquillian. In this example we inject a URI for the deployment.

In Container Test

In container tests have a similar structure to client based test. However, the test itself runs inside the container. This allows you to use CDI to inject beans into your test for example.

@ArquillianTest
@RequestScoped
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TaskRegistryTest {

    @Inject
    private TaskRegistry taskRegistry;

    @Deployment
    public static WebArchive createDeployment() {
        return ShrinkWrap.create(WebArchive.class)
                // Note for this test we don't use the REST endpoints so we don't need the REST resources
                .addClasses(TaskRegistry.class,
                        Priority.class,
                        Task.class,
                        Producers.class,
                        TaskListener.class)
                .addAsResource("META-INF/persistence.xml")
                .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @Test
    @Order(1)
    public void addTask(final TestInfo testInfo) {
        final Task task = new Task();
        task.setAdded(Instant.now());
        task.setDescription("This is a test task from " + testInfo.getTestMethod()
                .map(Method::getName)
                .orElse("<unknown>"));
        task.setPriority(Priority.IMPORTANT);
        task.setSummary("Test summary");
        final var addedTask = taskRegistry.add(task);
        Assertions.assertEquals(task, addedTask);
    }
}

What’s next?

Using JUnit 5 and Arquillian for testing offers several options for testing your application with WildFly. WildFly Arquillian includes some additional utilities not discussed in this guide such as the ability to configure server settings before your test executes. An advanced guide will dig deeper into the additional options for using Arquillian on WildFly.

< Back to Guides