Christian HagerSoftware Engineer // Technical Director // Photographer

Integration testing Rest-Services using Arquillian

In this post I want to demonstrate how Rest-Services can be integration tested using JBoss Arquillian1 and Glassfish-Embedded2. To do so we first need a Rest-Service which can be tested. So first of all we create a new maven-web-project and add the following rest resource. It is a very simple resource which returns a contact for a given identifier, but for our demo it will suffice.

@Path("/contacts")
public class ContactResource {

 private static final Logger LOG = LoggerFactory.getLogger(ContactResource.class);

 @Context
 UriInfo uriInfo;

 private HashMap<long, contact=""> contacts;

 public ContactResource() {
  contacts = new HashMap<long, contact="">();
  contacts.put(1L, new Contact(1L, "Bob"));
  contacts.put(2L, new Contact(1L, "Sam"));
 }

 @GET
 @Path("/{id}")
 @Produces(MediaType.APPLICATION_JSON)
 public Response get(@PathParam("id") Long id) {
  LOG.debug("Getting contact with id {id}", id);
  if (contacts.containsKey(id)) {
   LOG.debug("Contact found");
   return Response.ok(contacts.get(id)).build();
  }
  LOG.debug("No contact found");
  return Response.noContent().build();
 }

}

Now we should complete the setup of our service (configure the JAX-RS Servlet, add a logging configuration, …) and make sure it will deploy correctly.

At this point we are ready to add our integration test to the project. So we add a new JUnit-Test to our scr/test/resources folder. We name it ContactResourceIT and annotate it like shown in the following listing.

@RunWith(Arquillian.class)
public class ContactResourceIT {

You could also name the test differently but you should make sure that it still ends with IT (Integration Test) or else we would have to adapt the configuration of the maven-failsafe-plugin which we’ll add later to run our integration test.
The next thing we have to add to our test is a deployment assembly to build a war-archive which we can run our tests against. We do this by adding a method annotated with @Deployment to our test. In this method we use JBoss ShrinkWrap3 to assemble the war-archive.

@Deployment
public static WebArchive createArchiveAndDeploy() {
 WebArchive war = ShrinkWrap.create(WebArchive.class, "arquillian-rest-demo.war");
 war.addPackages(true, "de.nordiccoding.demos");
 war.addAsResource("META-INF/logback.xml");
 war.addAsWebInfResource(new File("src/main/webapp", "WEB-INF/web.xml"));
 war.addAsWebInfResource(new File("src/main/webapp", "WEB-INF/glassfish-web.xml"));
 File testLibDir = new File("target/test-libs/compile");
 for (File libFile : testLibDir.listFiles()) {
  war.addAsLibraries(libFile);
 }
 return war;
}

Finally we add some test methods to the test. The demo uses the Rest-assured4 framework to easily test the service but any other framework can be used as well.

@Test
public void testGetBob() {
 given().contentType(ContentType.JSON).expect().body("name", equalTo("Bob")).log().ifError().statusCode(200).when().get("http://localhost:8080/arquillianRestDemo/contacts/1");
}

@Test
public void testNothingFound() {
 given().contentType(ContentType.JSON).expect().log().ifError().statusCode(Status.NO_CONTENT.getStatusCode()).when().get("http://localhost:8080/arquillianRestDemo/contacts/3");
}

The following listing shows the complete integration test.

import static com.jayway.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;

import java.io.File;

import javax.ws.rs.core.Response.Status;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jayway.restassured.http.ContentType;

@RunWith(Arquillian.class)
public class ContactResourceIT {

 private static final Logger LOG = LoggerFactory.getLogger(ContactResourceIT.class);

 @Deployment
 public static WebArchive createArchiveAndDeploy() {
  WebArchive war = ShrinkWrap.create(WebArchive.class, "arquillian-rest-demo.war");
  war.addPackages(true, "de.nordiccoding.demos");
  war.addAsResource("META-INF/logback.xml");
  war.addAsWebInfResource(new File("src/main/webapp", "WEB-INF/web.xml"));
  war.addAsWebInfResource(new File("src/main/webapp", "WEB-INF/glassfish-web.xml"));
  File testLibDir = new File("target/test-libs/compile");
  for (File libFile : testLibDir.listFiles()) {
   war.addAsLibraries(libFile);
  }
  LOG.debug(war.toString(true));
  return war;
 }

 @Test
 public void testGetBob() {
  given().contentType(ContentType.JSON).expect().body("name", equalTo("Bob")).log().ifError().statusCode(200).when().get("http://localhost:8080/arquillianRestDemo/contacts/1");
 }

 @Test
 public void testNothingFound() {
  given().contentType(ContentType.JSON).expect().log().ifError().statusCode(Status.NO_CONTENT.getStatusCode()).when().get("http://localhost:8080/arquillianRestDemo/contacts/3");
 }
}

Before we can run the test we first have to add some more configuration to make arquillian and glassfish run correctly. First of all we will configure arquillian by placing a file named arquillian.xml in our src/test/resources folder. This configuration tells arquillian where to find the glassfish configuration and which port to bind the http-service to. The file should have the following content.

<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">
 <container qualifier="glassfish-embedded" default="true">
  <configuration>
   <property name="bindHttpPort">8080</property>
   <property name="configurationXml">src/test/resources/glassfish/domain.xml</property>
   <property name="configurationReadOnly">true</property>
  </configuration>
 </container>
</arquillian>

Next we have to add our glassfish configuration in form of a domain.xml file. For this demo we will use the domain.xml of a standard development installation. As we have configured in our arquillian.xml we place the domain.xml in the folder src/test/resources/glassfish.

Finally we have to some configurations to our maven-pom. First we add all necessary dependencies (arquillian, glassfish-embedded, logging,…).

<dependencyManagement>
 <dependencies>
  <dependency>
   <groupId>org.jboss.arquillian</groupId>
   <artifactId>arquillian-bom</artifactId>
   <version>1.0.0.Final</version>
   <scope>import</scope>
   <type>pom</type>
  </dependency>
 </dependencies>
</dependencyManagement>
<dependencies>
 <dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>0.9.9</version>
  <exclusions>
   <exclusion>
    <artifactId>slf4j-api</artifactId>
    <groupId>org.slf4j</groupId>
   </exclusion>
  </exclusions>
 </dependency>
 <dependency>
  <artifactId>slf4j-api</artifactId>
  <groupId>org.slf4j</groupId>
  <version>1.5.6</version>
 </dependency>
 <dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-json</artifactId>
  <version>1.11</version>
 </dependency>
 <dependency>
  <groupId>javax.xml.bind</groupId>
  <artifactId>jaxb-api</artifactId>
  <version>2.2</version>
 </dependency>
 <dependency>
  <groupId>com.sun.xml.bind</groupId>
  <artifactId>jaxb-impl</artifactId>
  <version>2.2.4</version>
 </dependency>
 <dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-server</artifactId>
  <version>1.11</version>
  <scope>provided</scope>
 </dependency>
 <dependency>
  <groupId>org.glassfish.main.extras</groupId>
  <artifactId>glassfish-embedded-all</artifactId>
  <version>3.1.2</version>
  <scope>provided</scope>
 </dependency>
 <dependency>
  <groupId>com.jayway.restassured</groupId>
  <artifactId>rest-assured</artifactId>
  <version>1.6</version>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.10</version>
  <scope>test</scope>
  <exclusions>
   <exclusion>
    <artifactId>hamcrest-core</artifactId>
    <groupId>org.hamcrest</groupId>
   </exclusion>
  </exclusions>
 </dependency>
 <dependency>
  <groupId>org.jboss.arquillian.junit</groupId>
  <artifactId>arquillian-junit-container</artifactId>
  <scope>test</scope>
 </dependency>
 <dependency>
  <groupId>org.jboss.arquillian.container</groupId>
  <artifactId>arquillian-glassfish-embedded-3.1</artifactId>
  <version>1.0.0.CR3</version>
  <scope>test</scope>
 </dependency>
</dependencies>

Now we configure the maven-failsafe-plugin which we will use to run our integration tests.

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-failsafe-plugin</artifactId>
 <version>2.12</version>
 <executions>
  <execution>
   <goals>
    <goal>integration-test</goal>
    <goal>verify</goal>
   </goals>
  </execution>
 </executions>
</plugin>

Last but not least we have to configure the maven-dependency-plugin to copy the necessary dependencies to the target folder. These libraries will be assembled into the war-archive by ShrinkWrap once we run the test.

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-dependency-plugin</artifactId>
 <version>2.4</version>
 <executions>
  <execution>
   <id>copy-test-libs</id>
   <phase>process-test-resources</phase>
   <configuration>
    <stripVersion>true</stripVersion>
    <outputDirectory>${project.build.directory}/test-libs</outputDirectory>
    <includeScope>compile</includeScope>
    <useSubDirectoryPerScope>true</useSubDirectoryPerScope>
   </configuration>
   <goals>
    <goal>copy-dependencies</goal>
   </goals>
  </execution>
 </executions>
</plugin>

At this point we should be able to run the test by executing mvn verify. By the way, the full source for the demo project can be found here.

Happy Testing…

Links

  1. JBoss Arquillian
  2. Glassfish Embedded
  3. JBoss ShrinkWrap
  4. Rest-assured

Tags: , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *