First Steps to Arquillian

Overview

Arquillian is an integration test framework that allows tests to be executed in a managed environment. In this blog post, arquillian will be used to test persistence functionality within WildFly 8.

Installation

Simply add the maven dependencies:

    <!-- Make Arquillian work with JUnit -->
    <dependency>
      <groupId>org.jboss.arquillian.junit</groupId>
      <artifactId>arquillian-junit-container</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.jboss.arquillian.protocol</groupId>
      <artifactId>arquillian-protocol-servlet</artifactId>
      <scope>test</scope>
    </dependency>

    <!-- For managing and wrapping of maven dependencies used by the application under test -->
    <dependency>
      <groupId>org.jboss.shrinkwrap.resolver</groupId>
      <artifactId>shrinkwrap-resolver-impl-maven</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.jboss.arquillian.extension</groupId>
      <artifactId>arquillian-persistence-dbunit</artifactId>
      <version>1.0.0.Alpha7</version>
      <scope>test</scope>
    </dependency>

A managed container where Arquillian will execute the tests is required. To do this, provide a default activated profile with the managed container declared as dependency. Since WildFly will be used, wildfly-arquillian-container-managed is declared as dependency.

  <profiles>
    <profile>
      <id>arq-wildfly-managed</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <dependencies>
        <dependency>
          <groupId>org.wildfly</groupId>
          <artifactId>wildfly-arquillian-container-managed</artifactId>
          <version>${version.arquillian.container}</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    </profile>  </profiles>

Arquillian configuration is declared in the arquillian.xml file in the test resource path.

<?xml version="1.0" encoding="UTF-8"?>
<arquillian xmlns="http://jboss.org/schema/arquillian"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://jboss.org/schema/arquillian
        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

  <!-- Force the use of the Servlet 3.0 protocol with all containers, as 
    it is the most mature -->
  <defaultProtocol type="Servlet 3.0" />

  <!-- configuration for wildfly instance. Will look at JBOSS_HOME environment variable for wildfly location -->
  <container qualifier="jboss" default="true">
  </container>

  <extension qualifier="persistence">
    <property name="defaultDataSource">java:jboss/datasources/TestDS</property>
  </extension>
</arquillian>

Since the location of WildFly server is not configured in the jboss configuration, the managed container will be looking for the WildFly server from the JBOSS_HOME environment variable. The following should be declared as an environment variable:
JBOSS_HOME=/path/to/wildfly

Deployment Declaration in JUnit

Arquillian tests do not differ much from ordinary JUnit tests. It just needs some additional configurations.

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.persistence.Cleanup;
import org.jboss.arquillian.persistence.CleanupStrategy;
import org.jboss.arquillian.persistence.TestExecutionPhase;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
@Cleanup(phase = TestExecutionPhase.AFTER, strategy = CleanupStrategy.USED_ROWS_ONLY)
public class SampleModelTest {

  @Deployment
  public static Archive<?> createDeployment() {

    return ShrinkWrap
            .create(WebArchive.class)
            .addAsLibraries(
                    Maven.resolver().loadPomFromClassLoaderResource("persistence-test-pom.xml")
                            .importRuntimeDependencies().resolve().withTransitivity().asFile())
            .addPackages(true, "com.sample.model")
            .addPackages(true, "com.sample.data")
            .addAsWebInfResource("h2test-ds.xml")
            .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
            .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
  }
  ...
}
  • Arquillian has a JUnit runner class and this can be activated by annotating the test class with @RunWith(Arquillian.class)
  • The static method @Deployment annotation indicates how the test class should be packaged before being deployed to wildfly for testing. This method must be a public static method.
  • ShrinkWrap is used to generate the archive file. For generating a war file, WarArchive.class must be specified in the create() method.
  • The libraries that the application under test will be using can be included in the package by calling addAsLibraries(). In the case of this example, persistence-test-pom.xml was created to declare the dependencies that will be used within this test.
  • Packages or classes that will be used in the test can be added to the web archive via addPackages() and addClasses(), respectively.
  • A data source can also be declared and added to the WEB-INF resource of the web archive so that it can be loaded by WildFly when the test archive is deployed to the server by Arquillian. Alternatively, the datasource can also be declared in WildFly via the admin console so that the addition of the h2test-ds.xml file can be omitted.
  • Since JPA is used by the application, META-INF/persistence.xml must be present in the classpath. For this example, the persistent unit is declared such that it is using the test data source.
  • Lastly, CDI is used by the example and thus beans.xml must be present in the web archive.

Only the basic dependencies need to be declared within the POM that will be used in the test.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <artifactId>persistence</artifactId>
  <groupId>com.test</groupId>
  <version>0.0.1-SNAPSHOT</version>
  <name>Sample</name>

  <properties>
    <deltaspike.version>1.2.1</deltaspike.version>
    <commons.beanutils.version>1.9.2</commons.beanutils.version>
  </properties>

  <dependencies>

    <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>${commons.beanutils.version}</version>
    </dependency>

    <dependency>
      <groupId>org.apache.deltaspike.core</groupId>
      <artifactId>deltaspike-core-api</artifactId>
      <version>${deltaspike.version}</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.deltaspike.modules</groupId>
      <artifactId>deltaspike-partial-bean-module-api</artifactId>
      <version>${deltaspike.version}</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.deltaspike.modules</groupId>
      <artifactId>deltaspike-partial-bean-module-impl</artifactId>
      <version>${deltaspike.version}</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.deltaspike.modules</groupId>
      <artifactId>deltaspike-jpa-module-api</artifactId>
      <version>${deltaspike.version}</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.deltaspike.modules</groupId>
      <artifactId>deltaspike-jpa-module-impl</artifactId>
      <version>${deltaspike.version}</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.deltaspike.core</groupId>
      <artifactId>deltaspike-core-impl</artifactId>
      <version>${deltaspike.version}</version>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.deltaspike.modules</groupId>
      <artifactId>deltaspike-data-module-api</artifactId>
      <version>${deltaspike.version}</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>org.apache.deltaspike.modules</groupId>
      <artifactId>deltaspike-data-module-impl</artifactId>
      <version>${deltaspike.version}</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<datasources xmlns="http://www.jboss.org/ironjacamar/schema"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.jboss.org/ironjacamar/schema http://docs.jboss.org/ironjacamar/schema/datasources_1_0.xsd">
   <!-- The datasource is bound into JNDI at this location. We reference 
      this in META-INF/test-persistence.xml -->
   <datasource jndi-name="java:jboss/datasources/TestDS"
      pool-name="test" enabled="true"
      use-java-context="true">
      <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</connection-url>
      <driver>h2</driver>
      <security>
         <user-name>sa</user-name>
         <password>sa</password>
      </security>
   </datasource>
</datasources>

A data source was loaded with the WAR file but it might be better to create a staging DB instance and declare a data source JNDI inside the WildFly server. This way, the JPA models within the code can be validated against the latest DB schema that will be loaded in a higher environment.

For this example, the arquillian persistence extension was used to easily use DBUnit to validate DB-related code.

@RunWith(Arquillian.class)
@Cleanup(phase = TestExecutionPhase.AFTER, strategy = CleanupStrategy.USED_ROWS_ONLY)
public class SampleModelTest {
  ...
  
  @Test
  @UsingDataSet("datasets/user-model.yml")
  @ShouldMatchDataSet("datasets/expected-user-model.yml")
  public void fieldsShouldMatch() {
  }
}
  • JUnit’s @Test annotation is used to indicate the test method.
  • @UsingDataSet is used to load data from a file. XML, JSON and YML formats can be used. In this example, YML was used.
  • @ShouldMatchDataSet is used to check the loaded data.

The following are the YML files:

user:
– id: 1
name: "NEW"
age: 30

user:
– name: "NEW"
age: 30

Conclusion

Arquillian provides the safety net to check if the application is compatible with the environment where it will be deployed to. The downside for this procedure is that the test takes longer to finish.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s