Arquillian and Wildfly: Integration test tutorial
You probably have already heard of Arquillian. But don’t know how to get started? We take you through a lightning tour of Arquillian tests and execute them on the Wildfly container. So buckle up and fasten your seat belts!
Arquillian is a testing platform for JavaEE applications. It eases testing of JavaEE code by eliminating the need for test mocks. It is possible because of an innovative idea – Each test is a micro-deployment on the application server containing the code to be tested. Since the test and the application code is running on the same container(JVM), it is possible to invoke the real code from the test instead of mock objects.
Although Arquillian could be used for Unit testing, its main utility is for integration testing where testing a piece of code in coordination with many components and across Transaction boundaries is necessary.
Arquillian gives you the ability to create a “production like” environment on the container and execute tests in this environment. This environment uses a real database, could contain data sources, JMS configurations, security settings and other configurations you expect to see in production environment. Because none of the tests are mocked, the tests reflect a real production environment and increase the confidence in the code.
Last but not the least, because Arquillian setup and test execution can be done using maven, it gives us the ability to repeatedly execute the tests, either manually or as a part of a Continuous Integration system.
Without further adieu, we present a simple Arquillian test setup using a managed Wildfly container.
What does this tutorial cover?
- We are basing this tutorial on the Wildfly container, the most used Java application server in the world.
- The Wildfly server will be used as a managed container i.e. the integration tests manage the container. Well, the tests will even download the Wildfly server, deploy the code on it and execute the tests! The tests will also start and stop the server on their own!
- At the end of this tutorial, you should have a solid understanding of how to implement and execute integration tests for your JavaEE project using Arquillian and Wildfly.
Step 1: The code to be tested
In this tutorial, we are going to test a JavaEE server-side component named LoginBean. The method login() accepts a name (for e.g. Mary) and returns the String “Hello, Mary”. The objective is to make sure that the method is indeed behaving as expected while deployed on the WildFly container.
Remember that there is no mocking involved, so we will be testing the login() method executing on a real Wildfly application server!
public class LoginBean {
public String login(String name) {
return "Hello, " + name;
}
}
Step 2: The Arquillian integration test
The integration test uses JUnit syntax. Each test is marked with the @Test annotation.
There are a few differences though.
Observe the annotation at the top of the class – @RunWith(Arquillian.class). It indicates that this is a test to be executed using Arquillian.
There is also a method createDeployment() marked with the annotation @Deployment. This method creates the package i.e the micro-deployment that will be installed on the managed server. As you can see, a JAR Archive is created with the code to be tested (i.e. LoginBean) and a beans.xml. The beans.xml is packaged into the archive so that dependency injection is enabled in the tests.
If LoginBean was in turn dependent on other classes, those classes also need to be included using the addClass() method. An alternative(not covered in this basic tutorial) is to use ShrinkWrapResolver that will automatically get all dependencies from the pom.xml.
The test is self-explanatory. See how we were able to inject an instance of LoginBean and invoke the login method! The results are verified using an Assert statement.
@RunWith(Arquillian.class)
public class LoginBeanTestIT {
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create(JavaArchive.class)
.addClass(LoginBean.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
@Inject
LoginBean loginBean;
@Test
public void login_mary() {
String loginResponse = loginBean.login("Mary");
Assert.assertEquals("Hello, Mary", loginResponse);
}
}
Step 3: The magic in the background
Most of the magic happens in the pom.xml. The Arquillian BOM covers all dependencies needed for Arquillian.
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>${version.arquillian}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
A profile named wildfly-managed is created just to manage execution of integration tests. As the name indicates, this profile only executes tests on the managed wildfly instance. Other profiles similar to this could be created for other container adapters such as remote servers or entirely different application servers such as Glassfish.
<profile>
<id>wildfly-managed</id>
....
An actual Wildfly instance is downloaded by the test itself. This is achieved by associating the unpack goal to the process-test-classes phase. In the tutorial, the wildfly distribution is unpacked in the target folder, so that it does not interfere with other servers and is deleted when mvn clean is executed.
<profile>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
<executions>
<execution>
<id>unpack</id>
<phase>process-test-classes</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-dist</artifactId>
<version>${version.wildfly}</version>
<type>zip</type>
<overWrite>false</overWrite>
<outputDirectory>target</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
And finally the integration tests are executed in the verify goal. The arquillian.launch property specifies the configuration for the wildfly managed container. This configuration is found in the arquillian.xml file. Various configurations(such as port number, admin usernames, passwords etc) could be specified in this file. In our example, we have chosen to use the default configuration.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.18.1</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<arquillian.launch>wildfly-managed</arquillian.launch>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<jboss.home>${project.basedir}/target/wildfly-8.2.0.Final</jboss.home>
</systemPropertyVariables>
<redirectTestOutputToFile>false</redirectTestOutputToFile>
</configuration>
</execution>
</executions>
</plugin>
Note that the qualifier name in arquillian.xml should match the name specified in arquillian.launch property.
Step 4: Execute the Arquillian test
To execute the tests, run the maven command
mvn clean install -Pwildfly-managed
The reports of the integration tests are created in the failsafe-reports folder in the target directory.
If the -Pwildfly-managed is not specified, the default profile executes without running the integration tests.
Further steps
In real world situation, it might be necessary that other configurations such as JMS, datasources etc are needed for the application to execute correctly. These are many ways in which to create them:
- Use other maven plugins within the wildfly-managed profile e.g. wildfly-maven-plugin to start/stop server or to create data sources
- Use a remote wildfly server instead of managed, this way the server can be pre-configured to have the required configuration
- Use managed wildfly as shown in the tutorial, but replace the standalone.xml (or other wildfly configuration files as appropriate) using maven to create the necessary configurations. Note that this is a risky proposition as it is not recommended to modify configuration files directly.
GitHub Code
The complete working code used in this tutorial is available in SoftwareYoga GitHub