1. Abstract

The WildFly JUnit Extension module (wildfly-junit-extension) provides the core testing framework with automatic server lifecycle management, deployment handling, and resource injection. It integrates with JUnit to provide a seamless testing experience for WildFly-based applications.

2. Overview

The extension automatically manages:

  • Server Lifecycle - Starts WildFly once before tests, stops after completion

  • Deployments - Manages per-test deployments and undeployments

  • Resource Injection - Injects server resources into test fields and parameters

  • Domain Mode - Supports testing on domain controllers

  • Manual Control - Optional manual server lifecycle management

3. Getting Started

3.1. Maven Dependency

Add the extension module to your test dependencies:

<dependency>
    <groupId>org.wildfly.testing</groupId>
    <artifactId>wildfly-junit-extension</artifactId>
    <version>${version.org.wildfly.testing}</version>
    <scope>test</scope>
</dependency>

3.2. Requirements

  • Java 17 or later

  • JUnit 6.0 or later

  • WildFly 27 or later

  • WildFly installation accessible via jboss.home property or JBOSS_HOME environment variable

3.3. Basic Example

@WildFlyTest
public class HelloWorldTest {

    @GenerateDeployment
    public static void createDeployment(final WebArchive deployment) {
        deployment.addClass(HelloServlet.class);
    }

    @Test
    public void testHello(@ServerResource URI uri) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(uri.resolve("hello"))
                .build();
        HttpResponse<String> response = client.send(request,
                HttpResponse.BodyHandlers.ofString());
        Assertions.assertEquals(200, response.statusCode());
    }
}

The @WildFlyTest annotation marks a test class that requires a WildFly server.

@WildFlyTest
public class MyTest {
    @Test
    public void test() {
        // WildFly server is automatically started
    }
}

3.4. Test Tags

Tests are automatically tagged to allow selective execution:

  • @WildFlyTest - tagged with wildfly-standalone

  • @WildFlyDomainTest - tagged with wildfly-domain

  • @ManualMode - tagged with wildfly-manual

Examples:

# Run only standalone tests
mvn test -Dgroups=wildfly-standalone

# Run only domain tests
mvn test -Dgroups=wildfly-domain

# Run only manual mode tests
mvn test -Dgroups=wildfly-manual

# Run standalone automatic mode tests (not manual)
mvn test -Dgroups=wildfly-standalone -DexcludedGroups=wildfly-manual

# Run all non-domain tests
mvn test -DexcludedGroups=wildfly-domain

4. Creating Deployments

4.1. @DeploymentProducer

Use @DeploymentProducer to create a deployment that returns an archive:

@WildFlyTest
public class DeploymentTest {

    @DeploymentProducer
    public static WebArchive deployment() {
        return ShrinkWrap.create(WebArchive.class, "test.war")
                .addClass(TestServlet.class)
                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @Test
    public void test() {
        // Test against deployed application
    }
}

4.2. @GenerateDeployment

Use @GenerateDeployment to create an empty archive which is inferred from the argument type:

@WildFlyTest
public class GenerateTest {

    @GenerateDeployment
    public static void generate(final WebArchive war) {
        war.addClass(TestServlet.class)
           .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @Test
    public void test() {
        // Test implementation
    }
}

The name for the deployment will be the name of the test class, Class.getSimpleName(), and the appropriate extension. In the above example, the deployment name would be GenerateTest.war.

A test can have either @GenerateDeployment or @DeploymentProducer, but not both.

5. Resource Injection

5.1. @ServerResource

Inject server resources into test fields or parameters:

@WildFlyTest
public class ResourceTest {

    @ServerResource
    private ServerManager serverManager;

    @ServerResource
    private URI uri;

    @Test
    public void test() {
        Assertions.assertTrue(serverManager.isRunning());
        Assertions.assertNotNull(uri);
    }
}

5.2. Available Resources

  • org.wildfly.plugin.tools.server.ServerManager - Server management interface

  • java.net.URI - HTTP URI to deployed application

  • org.jboss.as.controller.client.ModelControllerClient - A client which can talk to the management endpoint of the server

  • org.wildfly.plugin.tools.DeploymentManager - A deployment manager to deploy archives to the server

When injecting a ServerManager the start() and shutdown() methods will throw an exception if the test is not a @ManualMode[@ManualMode] test.

6. Server Lifecycle

6.1. Automatic Mode (Default)

By default, the extension:

  1. Starts WildFly before the first test

  2. Keeps it running for all tests

  3. Deploys/undeploys applications per test

  4. Stops the server after all tests complete

6.2. @ManualMode

Take control of the server lifecycle:

@WildFlyTest
@ManualMode
public class ManualTest {

    @ServerResource
    private static ServerManager serverManager;  // Must be static for @BeforeAll/@AfterAll

    @BeforeAll
    static void startServer() {
        serverManager.start();
    }

    @AfterAll
    static void stopServer() {
        serverManager.shutdown();
    }

    @Test
    public void test() {
        // Test with running server
        Assertions.assertTrue(serverManager.isRunning());
    }
}

7. Domain Mode

Test deployments on a domain controller:

@WildFlyDomainTest
public class DomainTest {

    @DeploymentProducer
    @ServerGroup("main-server-group")
    public static WebArchive deployment() {
        return ShrinkWrap.create(WebArchive.class)
                .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @ServerResource
    @DomainServer("server-one")
    private URI uri;

    @Test
    public void test() {
        // Test against domain deployment
    }
}

7.1. Domain Mode Requirements

To use domain mode testing:

  1. Know your server group name - Check domain.xml for available server groups:

    <server-groups>
        <server-group name="main-server-group" profile="full">
            <!-- ... -->
        </server-group>
    </server-groups>
  2. Know your server names - Check host.xml for servers in the group:

    <servers>
        <server name="server-one" group="main-server-group">
            <!-- ... -->
        </server>
    </servers>
  3. Use the correct annotations:

    • @WildFlyDomainTest on the test class

    • @ServerGroup("server-group-name") on the deployment method (you can specify multiple server groups: @ServerGroup({"group1", "group2"}))

    • @DomainServer("server-name") on @ServerResource fields to target specific servers

Domain tests are automatically tagged with @Tag("wildfly-domain"). You can use this to run domain tests separately: mvn test -Dgroups=wildfly-domain

8. ServerManager API

The ServerManager interface provides methods for controlling and querying the WildFly server:

8.1. Lifecycle Methods

// Start the server
serverManager.start();

// Check if server is running
boolean running = serverManager.isRunning();

// Shutdown the server
serverManager.shutdown();

// Kill the server process (forceful)
serverManager.kill();

8.2. Deployment Operations

// Deploy an archive
DeploymentResult result = serverManager.deploy(archive);

// Undeploy by name
serverManager.undeploy(deploymentName);

// Redeploy
serverManager.redeploy(archive);

8.3. Management Operations

Execute management operations using the WildFly management API:

// Read an attribute
ModelNode op = Operations.createReadAttributeOperation(
    Operations.createAddress("subsystem", "logging"),
    "add-logging-api-dependencies"
);
ModelNode result = serverManager.executeOperation(op);

// Write an attribute
op = Operations.createWriteAttributeOperation(
    Operations.createAddress("system-property", "my.property"),
    "value",
    "my-value"
);
serverManager.executeOperation(op);

8.4. Server Control

// Reload the server
serverManager.reload();

// Wait for server to be ready
serverManager.waitFor(30, TimeUnit.SECONDS);

// Take a configuration snapshot
String snapshotName = serverManager.takeSnapshot();
See the ServerManager JavaDoc for the complete API reference.

This chapter covers all configuration options for WildFly JUnit Extension.

9. Configuration Properties Reference

Property Type Description Default

jboss.home

Path

WildFly installation directory

$JBOSS_HOME env var

wildfly.java.home

Path

Java Runtime Environment path

Current JVM (java.home)

wildfly.module.path

Path

JBoss Modules path

${jboss.home}/modules

wildfly.timeout

Integer (seconds)

Server startup/shutdown timeout

60

wildfly.http.protocol

String

HTTP protocol (http or https)

http

wildfly.http.host

String

HTTP host

localhost

wildfly.http.port

Integer

HTTP/HTTPS port

8080 (HTTP), 8443 (HTTPS)

wildfly.java.opts

String

Additional JVM arguments for WildFly

None

wildfly.debug

boolean

An empty value or a value of true indicates the server should be started up with remote debugging enabled.
NOTE: This property only works for standalone mode.

false

wildfly.debug.port

int

If wildfly.debug is enabled, the port the remote debugger should listen on.

8787

wildfly.debug.suspend

int

If wildfly.debug is enabled and the value is empty or set to true, suspend=y will be passed to the remote debug arguments. Otherwise, suspend=n will be passed.

true

10. System Properties

Configuration can be set via system properties or JUnit Platform configuration parameters.

10.1. Server Location

jboss.home

The path to the WildFly installation directory. If not set, falls back to the JBOSS_HOME environment variable, then jboss.home.dir property.

mvn test -Djboss.home=/opt/wildfly

10.2. Java Runtime

wildfly.java.home

Path to a different Java Runtime Environment. Defaults to the current JVM (java.home).

mvn test -Dwildfly.java.home=/usr/lib/jvm/java-21

10.3. Java Options

wildfly.java.opts

Additional JVM arguments passed to the WildFly server process. Multiple arguments can be space-separated. Use quotes for arguments containing spaces or special characters.

# Single option
mvn test -Dwildfly.java.opts="-Xmx1024m"

# Multiple options
mvn test -Dwildfly.java.opts="-Xmx1024m -XX:+UseG1GC"

# Options with commas or complex values (use quotes)
mvn test -Dwildfly.java.opts="\"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8787\""

10.4. Module Path

wildfly.module.path

Alternate module path for JBoss Modules. Defaults to ${jboss.home}/modules.

mvn test -Dwildfly.module.path=/custom/modules

10.5. Timeouts

wildfly.timeout

Timeout in seconds for server startup and shutdown operations. Default: 60.

mvn test -Dwildfly.timeout=120

10.6. HTTP Configuration

wildfly.http.protocol

Protocol for HTTP connections. Can be http or https. Default: http.

When set, the framework configures the WildFly server to listen on the corresponding port using the appropriate jboss.http.port or jboss.https.port system property.

wildfly.http.host

Host for HTTP connections used in URI resolution. Default: localhost.

wildfly.http.port

Port for HTTP connections. Default: 8080 for HTTP, 8443 for HTTPS.

The framework passes this to WildFly as -Djboss.http.port or -Djboss.https.port depending on the protocol.

# HTTP on custom port
mvn test -Dwildfly.http.port=8180

# HTTPS with default port
mvn test -Dwildfly.http.protocol=https

# HTTPS on custom port
mvn test -Dwildfly.http.protocol=https -Dwildfly.http.port=9443

11. JUnit Configuration

Properties can also be set in junit-platform.properties:

jboss.home=/opt/wildfly
wildfly.timeout=120
wildfly.http.port=8080

12. Maven Configuration

Configure properties in your pom.xml:

<properties>
    <jboss.home>${project.build.directory}/wildfly</jboss.home>
    <wildfly.timeout>120</wildfly.timeout>
</properties>

13. Configuration Examples

13.1. Local Development

For local development with a standard WildFly installation:

# src/test/resources/junit-platform.properties
jboss.home=/opt/wildfly-38.0.1.Final
wildfly.timeout=60

13.2. CI/CD Pipeline

For continuous integration with provisioned server:

<!-- pom.xml -->
<properties>
    <jboss.home>${project.build.directory}/wildfly</jboss.home>
    <wildfly.timeout>180</wildfly.timeout>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <systemPropertyVariables>
                    <jboss.home>${jboss.home}</jboss.home>
                    <wildfly.timeout>${wildfly.timeout}</wildfly.timeout>
                </systemPropertyVariables>
            </configuration>
        </plugin>
    </plugins>
</build>

13.3. Custom Ports

When default ports are unavailable or testing with HTTPS:

# Custom HTTP port
mvn test -Dwildfly.http.port=8180

# HTTPS on custom port
mvn test -Dwildfly.http.protocol=https -Dwildfly.http.port=9443

Or in junit-platform.properties:

wildfly.http.protocol=https
wildfly.http.port=8443

13.4. Remote Debugging

Enable remote debugging of WildFly during tests:

mvn test -Dwildfly.debug

Then attach your IDE debugger to port 8787.

The debug agent argument contains commas, so it must be quoted to prevent argument splitting.

13.5. Different Java Version

Test with a specific Java version:

mvn test -Dwildfly.java.home=/usr/lib/jvm/java-21-openjdk

14. Test-Specific Configuration

Override configuration in individual tests using @TestPropertySource or similar JUnit mechanisms.

15. WildFly Provisioning

When using the wildfly-maven-plugin to provision a server:

<plugin>
    <groupId>org.wildfly.plugins</groupId>
    <artifactId>wildfly-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>provision</goal>
            </goals>
            <configuration>
                <provisioning-dir>${project.build.directory}/wildfly</provisioning-dir>
                <feature-packs>
                    <feature-pack>
                        <groupId>org.wildfly</groupId>
                        <artifactId>wildfly-ee-galleon-pack</artifactId>
                        <version>${version.wildfly}</version>
                    </feature-pack>
                </feature-packs>
            </configuration>
        </execution>
    </executions>
</plugin>

Set jboss.home to the provisioning directory:

<properties>
    <jboss.home>${project.build.directory}/wildfly</jboss.home>
</properties>

This chapter covers advanced features and extensibility points in WildFly JUnit Extension.

16. Custom Server Configurations

16.1. Standalone Configuration

Implement StandaloneConfigurationFactory to customize standalone server configuration:

@MetaInfServices
public class CustomStandaloneConfig implements StandaloneConfigurationFactory {

    @Override
    public ServerConfiguration create(ExtensionContext context) {
        return ServerConfiguration.builder()
                .configFile("standalone-custom.xml")
                .jvmArgs("-Xmx2g", "-XX:+UseG1GC")
                .build();
    }
}

16.2. Domain Configuration

Implement DomainConfigurationFactory for domain mode:

@MetaInfServices
public class CustomDomainConfig implements DomainConfigurationFactory {

    @Override
    public ServerConfiguration create(ExtensionContext context) {
        return ServerConfiguration.builder()
                .configFile("domain-custom.xml")
                .hostConfigFile("host-custom.xml")
                .build();
    }
}

17. Server Management Operations

Execute management operations using the ServerManager:

@WildFlyTest
@ManualMode(true)
public class ManagementTest {

    @ServerResource
    private static ServerManager serverManager;

    @Test
    public void testManagementOperation() throws Exception {
        // Read system property
        ModelNode op = Operations.createReadAttributeOperation(
                Operations.createAddress("system-property", "test.property"),
                "value"
        );
        ModelNode result = serverManager.executeOperation(op);
        Assertions.assertTrue(Operations.isSuccessfulOutcome(result));

        // Write system property
        op = Operations.createWriteAttributeOperation(
                Operations.createAddress("system-property", "test.property"),
                "value",
                "newValue"
        );
        result = serverManager.executeOperation(op);
        Assertions.assertTrue(Operations.isSuccessfulOutcome(result));
    }
}

18. Taking Snapshots

Save and restore server configuration:

@WildFlyTest
@ManualMode(true)
public class SnapshotTest {

    @ServerResource
    private static ServerManager serverManager;
    private static String restorePath;

    @BeforeAll
    static void snapshot() throws Exception {
        restorePath = serverManager.takeSnapshot();
    }

    @AfterAll
    static void restore() throws Exception {
        if (restorePath != null) {
            ModelNode op = Operations.createOperation("reload");
            op.get("server-config").set(restorePath);
            serverManager.executeReload(op);
            serverManager.waitFor(30L, TimeUnit.SECONDS);
            serverManager.executeOperation(Operations.createOperation("write-config"));
        }
    }

    @Test
    public void testWithConfigChanges() {
        // Make configuration changes
        // They will be reverted after test suite completes
    }
}

19. Best Practices

19.1. Resource Management

  • Use @ServerResource with static fields in @ManualMode(true) tests when managing server lifecycle in @BeforeAll/@AfterAll

  • Close client resources properly in @AfterEach or try-with-resources

  • Take snapshots before making configuration changes

19.2. Performance

  • Minimize server restarts by using per-test deployments instead of server restarts

  • Use @ManualMode only when necessary

  • Share server instances across test classes when possible

19.3. Test Organization

  • Group tests by deployment requirements

  • Use @Tag annotations to categorize tests (e.g., domain tests, manual mode tests)

    • @WildFlyTest has a tag with the value "wildfly-standalone"

    • @WildFlyDomainTest has a tag with the value "wildfly-domain"

    • @ManualMode has a tag with the value "wildfly-manual"

  • Keep test deployments small and focused

19.4. Debugging

Enable debug logging for the framework by setting the org.wildfly.testing logger to DEBUG/FINE level in your logging configuration. The exact configuration depends on your logging framework (Java Util Logging, Log4j2, Logback, etc.).

Run tests with remote debugging:

mvn test -Dwildfly.debug

Appendix A: API Reference

For complete API documentation, see the JavaDoc API Documentation and WildFly Plugin Tools Documentation.