Testable development of CDI

Tutorial - Arquillian makes testing a breeze

Arquillian is a platform that simplifies integration testing for Java middleware. It deals with all the plumbing of container management, deployment and framework initialization so you can focus on the task at hand, writing your tests. Real tests. Arquillian minimizes the burden on you – the developer – by covering aspects surrounding test execution, including:

  • Managing the lifecycle of the container (start/stop),
  • Bundling the test class with dependent classes and resources into a deployable archive,
  • Enhancing the test class (e.g., resolving @Inject, @EJB and @Resource injections),
  • Deploying the archive to test (deploy/undeploy) and
  • Capturing results and failures.

To avoid introducing unnecessary complexity into the developer’s build environment, Arquillian integrates seamlessly with familiar testing frameworks (e.g., JUnit 4, TestNG 5), allowing tests to be launched using existing IDE, Ant and Maven test plug-ins – without any add-ons. This guide introduces you to Arquillian. After reading this guide, you will be able to:

  • Add the Arquillian infrastructure to a Maven-based Java project
  • Write an Arquillian test case to assert the behaviour of a CDI bean
  • Execute the Arquillian test case in multiple compatible containers

You’ll learn all of these skills by incorporating Arquillian into the test suite of a Java EE application with a Maven build. We’ve designed this guide to be a fast read to get you started quickly!

Assumptions

The easiest way to get started with Arquillian is to incorporate it into the test suite of a project build that offers dependency management. Today, the most widely-used build tool in this category is Apache Maven. This guide will navigate you to your first green bar using a sample Maven project.

Arquillian does not depend on Maven, or any specific build tool for that matter. It works just as well – if not better – when used in a project with an Ant or Gradle build. Ideally, the build tool should offer dependency management as it simplifies the task of including the Arquillian libraries since they are distributed in the Maven Central repository

This guide assumes you have Maven available, either in your command shell or your IDE. If you don’t, please install Maven now. You will also need JDK 1.6 or better installed on your machine.

Create a new project

There are two ways we recommend you create a new Maven project:

If you already have a Maven project, you can use this section as review to ensure you have the proper dependencies before moving on.

By far, JBoss Forge is the simpler approach, but this guide will offer both options in the event you aren’t ready to adopt JBoss Forge. Select from one of the two options above to jump to the instructions.

Generate a project from a Maven archetype

First, create a Maven-based Java project using the command below. Copy the text after the $ and paste it into your command shell.

 

$ mvn archetype:generate -DarchetypeGroupId=net.avh4.mvn.archetype \
-DarchetypeArtifactId=java-1.6-archetype

Respond to the prompts by entering the value shown after each double colon below. Hit the Enter key after each line (as indicated by <ENTER>).

Define value for property groupId: : org.arquillian.example <ENTER>

Define value for property artifactId: : arquillian-tutorial <ENTER>

Define value for property version: : <ENTER>

Define value for property package: : <ENTER>

Confirm properties configuration:

groupId: org.arquillian.example

artifactId: arquillian-tutorial

version: 1.0-SNAPSHOT

package: org.arquillian.example

Y: : <ENTER>

This command generated a Maven-based Java project inside a new folder named arquillian-tutorial under the current directory. The file structure of the project is shown below:

  • src/
    • main/
      • java/ – Place all application Java source files here (under Java package)
      • resources/ – Place all application configuration files here
    • test/
      • java/ – Place all test Java source files heres (under Java package)
      • resources/ – Place all test configuration files here (e.g., arquillian.xml)
  • pom.xml – The Maven build file. Tells Maven how your project should be built.

The project is preconfigured to use Java 6 and JUnit 4.8, the minimum required versions of Java and JUnit for using Arquillian, respectively.

The generator also created a Java package named org.arquillian.example underneath the two java folders. You should put your Java source files in this package rather than at the root of the java folder.

Arquillian also supports TestNG 5. However, we will be using JUnit throughout this guide.

Go ahead and open up the pom.xml in your editor. You should see an XML file containing basic project information, a build section and a dependencies section. You can remove all the elements below the JUnit dependency as they aren’t required.

After making the change, you should end up with the contents seen in listing 1 (trimmed for brevity).

Listing 1

pom.xml
<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/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.arquillian.example</groupId>
   <artifactId>arquillian-tutorial</artifactId>
   <version>1.0-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>arquillian-tutorial</name>
   <build>
       <plugins>
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>2.3.2</version>
               <configuration>
                   <source>1.6</source>
                   <target>1.6</target>
               </configuration>
           </plugin>
       </plugins>
   </build>
   <dependencies>
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.8.1</version>
           <scope>test</scope>
       </dependency>
   </dependencies>
</project>

We’re going to be writing Java EE 6 components. Therefore, we also need to add the Java EE 6 API to the classpath so we can compile these components.

Open up the pom.xml file once again and add the following XML fragment directly inside the <dependencies> element. Listing 2 shows how the section should look once you’re done.

Listing 2

pom.xml
<!-- clip -->
<dependencies>
   <dependency>
       <groupId>org.jboss.spec</groupId>
       <artifactId>jboss-javaee-6.0</artifactId>
       <version>1.0.0.Final</version>
       <type>pom</type>
       <scope>provided</scope>
   </dependency>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.8.1</version>
       <scope>test</scope>
   </dependency>
</dependencies>
<!-- clip -->

We strongly recommend that you do not use the Java EE API artifact with coordinatesjavax:javaee-api. That bundle contains classes with stripped method bodies, which will cause your application to throw strange Absent Code errors if left on the classpath at runtime (even when running tests). Read the FAQ under if you want more background.

The foundation of your project is now ready! Skip to the next section to open the project in Eclipse so we can start writing some code!

Create a Project Using Forge

JBoss Forge is a command shell for rapid-application development in a standards-based environment. Another way to think of it is that it’s like Maven Archetypes on steroids.

Installing Forge is a relatively short process, and this guide will take you through the fundamentals. Follow these simple steps to get it installed:

  1. Download Forge and unzip it into a folder on your harddrive, which we’ll call $FORGE_HOME
  2. We’ll assume you extracted the distribution to a folder named forge in your home directory
  3. Add $FORGE_HOME/bin to your path (Windows, Linux or Mac OSX)

On Unix-based operating systems, adding Forge to your path typically means editing your $HOME/.bashrc or $HOME/.profile; you will need to set the following environment variables:

$ export FORGE_HOME=$HOME/forge/

$ export PATH=$PATH:$FORGE_HOME/bin

On Windows, you will need to right-click on Control Panel, then click System Properties, open the Advanced tab, then click Environment Variables and add these two entries visually. We recommended setting User variables for Forge, unless you have placed the unzipped distribution in a folder where all users can access it.

Now that Forge is installed (i.e., extracted), open a command prompt (a shell) and run the forge command (figure 1):

$ forge 
[no project] ~ $

That’s it! You’ve got Forge up and running. Now it’s time to create the project.

Inside the Forge shell, execute the following command to create a blank project, much like we created a project using the Maven Archetype above:

$ new-project --named arquillian-tutorial --topLevelPackage org.arquillian.example

This command generates a Maven-based Java project inside a new folder named arquillian-tutorial under the current directory. The file structure of the project Forge generates is shown below:

  • src/
    • main/
      • java/ – Place all application Java source files here (under Java package)
      • resources/ – Place all application configuration files here
        • META-INF/
          • forge.xml – An empty Forge settings file
    • test/
      • java/ – Place all test Java source files heres (under Java package)
      • resources/ – Place all test configuration files here (e.g., arquillian.xml)
  • pom.xml – The Maven build file. Tells Maven how your project should be built.

Forge also makes the project folder your current directory within the Forge shell.

[arquillian-tutorial] arquillian-tutorial $

By default, Forge sets up the project to use Java 1.6, the minimum required version of Java to use Arquillian, a nice convenience. What we need to add now is the Java EE APIs. That’s done using the project add-dependency command below:

      $ project add-dependency org.jboss.spec:jboss-javaee-
6.0:1.0.0.Final:pom:provided

You will also need to add JUnit 4.8, the minimum required version of JUnit to use Arquillian, as a test-scoped dependency:

$ project add-dependency junit:junit:4.8.1:test

The result of the pom.xml that Forge generates is shown in Listing 3.

Listing 3

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
   xsi:schemaLocation="
       http://maven.apache.org/POM/4.0.0
       http://maven.apache.org/xsd/maven-4.0.0.xsd"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.arquillian.example</groupId>
   <artifactId>arquillian-tutorial</artifactId>
   <version>1.0.0-SNAPSHOT</version>
   <dependencies>
       <dependency>
           <groupId>org.jboss.spec</groupId>
           <artifactId>jboss-javaee-6.0</artifactId>
           <version>1.0.0.Final</version>
           <type>provided</type>
           <scope>pom</scope>
       </dependency>
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.8.1</version>
           <scope>test</scope>
       </dependency>
   </dependencies>
   <repositories>
     <repository>
         <id>JBOSS_NEXUS</id>
         <url>http://repository.jboss.org/nexus/content/groups/public</url>
     </repository>
   </repositories>
   <build>
       <finalName>arquillian-tutorial</finalName>
       <plugins>
           <plugin>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>2.3.2</version>
               <configuration>
                   <source>1.6</source>
                   <target>1.6</target>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

Arquillian is now distributed in the Maven Central repository, so the JBoss Public repository declaration in the pom.xml is unnecessary and can be removed. Keep in mind, though, you may need it to retrieve other JBoss software not available in Maven Central. If you follow the camp that consider repositories explicitly defined in your project’s pom.xml to be an antipattern, then read the instructions under  to enable the repository globally in your settings.xml file.

The foundation of your project is now ready! Let’s now open the project in Eclipse so we can start writing some code!

Open the Project in Eclipse

When developing a Java project, you’ll likely use an IDE, such as Eclipse. That’s why Arquillian has been designed to be IDE friendly, meaning you can run Arquillian tests from the IDE without making unconventional changes. So let’s start taking advantage of the IDE immediately.

Begin by launching Eclipse. Now, since this is a Maven project, you need the Maven Integration for Eclipse(m2e) plug-in installed to open the project. If you don’t already have the integration installed, the simplest way to get it is to install JBoss Tools. Follow these steps to install it from the Eclipse Marketplace (sort of like the app store for Eclipse).

  1. Select Help | Eclipse Marketplace... from the main menu
  2. Type “jboss tools” in the Find input field (no quotes) and press Enter
  3. Click the Install button next to JBoss Tools (Indigo)
  4. Complete the install wizard, then restart Eclipse if prompted

JBoss Tools provides a nice environment for developing Java EE applications, including excellent CDI support. Don’t worry, it’s not a heavyweight plugin.

However, if you just want the Maven integration without the extras that JBoss Tools brings, you can follow these steps instead:

  1. Select Help | Eclipse Marketplace... from the main menu
  2. Type “maven” in the Find input field (no quotes) and press Enter
  3. Click the Install button next to Maven Integration for Eclipse
  4. Complete the install wizard, then restart Eclipse if prompted
  5. Repeat the steps to install the Maven Integration for Eclipse WTP

Once you have the Maven Integration plugin installed, follow these steps to open the project:

  1. Select File | Import... from the main menu
  2. Type “existing maven” in the input source field
  3. Select the option Existing Maven Projects, then click the Next button
  4. Click the Browse… button
  5. Navigate the project folder on your filesystem, then click the OK button
  6. Click the Finish button to open the project

Eclipse will recognize the Maven project and open it in the Project Navigator view. If you expand the project, it should look similar to figure 2. Now we can really get down to business!

Create a Component

In order to write an Arquillian test, we need to have a component for it to test. Let’s begin by creating a basic component so that you can learn how to execute an Arquillian test without other distractions. We’ll gradually move to more complex scenarios.

In your IDE, create a new Java class named Greeter in the org.arquillian.example package. Replace the contents of the file with this greeter logic (Listing 4).

Listing 4

 

src/main/java/org/arquillian/example/Greeter.java
package org.arquillian.example;

import java.io.PrintStream;

/**
* A component for creating personal greetings. 
*/
public class Greeter {
   public void greet(PrintStream to, String name) {
       to.println(createGreeting(name));
   }

   public String createGreeting(String name) {
       return "Hello, " + name + "!";
   }
}

 

We want to verify that this class behaves properly when invoked as a CDI bean. Of course, we could simply write a unit test. But let’s pretend that the bean uses enterprise services such as dependency injection and messaging and must be used inside a container. (Besides, that way we give it room to grow).

To use the class as a CDI bean, we’ll be injecting it into the test using the @Inject annotation. That calls for an Arquillian test, which means it’s time to add the Arquillian API to the project!

Add the Arquillian APIs

Once again open up the pom.xml file, located in the root folder of the project, in your editor. We need to instruct Maven which versions of the artifacts to use. Insert the following XML fragment directly above the <build> element to import the BOM, or version matrix, for Arquillian’s transitive dependencies.

 

pom.xml
<!-- clip -->
<dependencyManagement>
   <dependencies>
       <dependency>
           <groupId>org.jboss.arquillian</groupId>
           <artifactId>arquillian-bom</artifactId>
           <version>1.0.0.CR7</version>
           <scope>import</scope>
           <type>pom</type>
       </dependency>
   </dependencies>
</dependencyManagement>
<!-- clip -->

 

Next, append the following XML fragment directly under the last <dependency> element to add the Arquillian JUnit integration:

 

pom.xml
<!-- clip -->
<dependency>
   <groupId>org.jboss.arquillian.junit</groupId>
   <artifactId>arquillian-junit-container</artifactId>
   <scope>test</scope>
</dependency>
<!-- clip -->

 

The Arquillian JUnit integration artifact also adds the Arquillian and ShrinkWrap APIs to the test classpath. You need all of these libraries to write and compile a JUnit Arquillian test. To use TestNG instead of JUnit, substitute the Arquillian JUnit integration with the Arquillian TestNG integration. If you’re having trouble with the pom.xml up to this point, you can download the file from the gist. You’re all set to write your first Arquillian test!

Write an Arquillian Test

An Arquillian test looks just like a unit test, just with some extra flair. Let’s return to the IDE. If you get the message “Project configuration is out of date with pom.xml”, then right click and selectProject | Maven | Update Project Configuration to resync the project.

Begin by creating a new JUnit test case in src/test/java under the org.arquillian.example package and name it GreeterTest. You won’t need the typical setup and teardown methods since Arquillian is doing most of the heavy lifting. Here’s what we have so far:

src/test/java/org/arquillian/example/GreeterTest.java
package org.arquillian.example;

import org.junit.Assert;
import org.junit.Test;

public class GreeterTest {
   @Test
   public void should_create_greeting() {
       Assert.fail("Not yet implemented");
   }
}

Now, about that flair. An Arquillian test case must have three things:

  1. A @RunWith(Arquillian.class) annotation on the class
  2. A static method annotated with @Deployment that returns a ShrinkWrap archive
  3. At least one method annotated with @Test

The @RunWith annotation tells JUnit to use Arquillian as the test controller. Arquillian then looks for a static method annotated with the @Deployment annotation to retrieve the test archive (i.e., micro-deployment). Then some magic happens and each @Test method is run inside the container environment.

What’s a test archive?

The purpose of the test archive is to isolate the classes and resources which are needed by the test from the remainder of the classpath. Unlike a normal unit test, Arquillian does not simply dip into the entire classpath. Instead, you include only what the test needs (which may be the entire classpath, if that’s what you decide). The archive is defined using ShrinkWrap, which is a Java API for creating archives (e.g., jar, war, ear) in Java. The micro-deployment strategy let’s you focus on precisely the classes you want to test and, as a result, the test remains very lean.

ShrinkWrap also supports resolving artifacts (libraries) and create configuration files programmatically, which can then be added to the test archive. For a more thorough introduction to ShrinkWrap, see the ShrinkWrap introduction guide. Let’s add that Arquillian flair to the test (listing 5).

src/test/java/org/arquillian/example/GreeterTest.java
package org.arquillian.example;

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.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class GreeterTest {

   @Deployment
   public static JavaArchive createDeployment() {
       return ShrinkWrap.create(JavaArchive.class)
           .addClass(Greeter.class)
           .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
   }

   @Test
   public void should_create_greeting() {
       Assert.fail("Not yet implemented");
   }
}

Using ShrinkWrap, we’ve defined a Java archive (jar) as the deployment that includes the Greeter class that the test will invoke and an empty beans.xml in the META-INF directory to activate CDI in this archive.

Now all we need to do is inject the Greeter instance into a field directly above the test method and replace the unimplemented test method with one that asserts the behavior of the bean. To give you that warm fuzzy feeling, we’ll also print the greeting to the console.

src/test/java/org/arquillian/example/GreeterTest.java
// clip
@Inject
Greeter greeter;

@Test
public void should_create_greeting() {
   Assert.assertEquals("Hello, Earthling!",
       greeter.createGreeting("Earthling"));
   greeter.greet(System.out, "Earthling");
}

Listing 6 shows how the test should look when you’re done.

Listing 6

src/test/java/org/arquillian/example/GreeterTest.java
package org.arquillian.example;

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.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.Assert;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class GreeterTest {

   @Deployment
   public static JavaArchive createDeployment() {
       return ShrinkWrap.create(JavaArchive.class)
           .addClass(Greeter.class)
           .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
   }

   @Inject
   Greeter greeter;

   @Test
   public void should_create_greeting() {
       Assert.assertEquals("Hello, Earthling!",
           greeter.createGreeting("Earthling"));
       greeter.greet(System.out, "Earthling");
   }
}

You’ve written your first Arquillian test! Ah, but you’re probably wondering how to run it. If you’re thinking, “Just like a unit test” you’re correct! However, we first need to add a container adapter to the classpath.

Add a Container Adapter

We’ve been talking a lot about testing in a container, but so far we have not mentioned which one. That’s because it’s a runtime decision. Arquillian selects the target container based on which container adapter is available on the test classpath. That means we’ll be adding more libraries to the project.

An Arquillian test can be executed in any container that is compatible with the programming model used in the test (as long as the container has an Arquillian adapter). Our test is using the CDI programming model, so we need to use any container to supports CDI. We want fast turnaround during development, so we’ll start with the Weld EE embedded container.

Open the pom.xml file again and add the following group of dependencies directly below the other<dependency> elements (listing 7).

Lisiting 7

pom.xml
<!-- clip -->
<dependency>
   <groupId>org.jboss.arquillian.container</groupId>
   <artifactId>arquillian-weld-ee-embedded-1.1</artifactId>
   <version>1.0.0.CR3</version>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>org.jboss.weld</groupId>
   <artifactId>weld-core</artifactId>
   <version>1.1.1.Final</version>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-simple</artifactId>
   <version>1.5.10</version>
   <scope>test</scope>
</dependency>
<!-- clip -->

To summarize, here are the three libraries you need to use Arquillian (with JUnit):

  1. Arquillian JUnit integration
  2. Arquillian container adapter for the target container
  3. Container runtime (embedded container) or container client (remote container)

We’re using an embedded container in this example, so we need the container runtime, Weld.

Now back to the test.

  

Pages

Dan Allen, Andrew Rubinger, Aslak Knutsen

What do you think?

JAX Magazine - 2014 - 03 Exclucively for iPad users JAX Magazine on Android

Comments

Latest opinions