Assumptions:
For the purpose of this article, we assume that the you are already familiar with Maven, JUnit already has a CDI-based project set up, and is proficient enough to add new dependencies to their project POM file. (Click here for a quick tutorial on getting started with CDI/Weld.)Process
This article will first provide a quick overview of unit-testing in general, then provide a contrasting example of unit-testing using the advanced capabilities in Arquillian, and a few thoughts on why/when you’d want to use it. With that said — let’s get started!In general, when writing comprehensive unit tests:
- All implementation classes should be accompanied by a unit test
- Classes should not be checked in until tests are provided
- Tests are code, make sure they are clear and make sense. If they do not, chances are that the architecture needs to be re-thought.
Set up JUnit in your Maven POM
Maven suggests strict and specific testing practices. All unit tests should be placed in the /src/test/java
folder, under the corresponding package to the class under test. This does not change when using Arquillian and Maven.
<properties>
<version.junit>4.10</version.junit>
</properties>
<dependencies>
<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${version.junit}</version>
<scope>test</scope>
</dependency>
</dependencies>Write your first plain JUnit test:
Tests are run automatically during a Maven build, such asmvn test,
mvn package, or
mvn install. It is important to note, however, that tests are only run if the name of the test class ends in ‘Test’ — e.g: SimpleMathTest.java Note the simplicity of the unit test. Each test-case should have only one class under test. E.g: The SimpleMathTest should only test behavior of ‘MathImpl.java’… Mixing testable classes in a test-case can lead to missed scenarios, bugs.
public interface Math
{
public int add(int a, int b);
public int subtract(int a, int b);
}public class MathImpl implements Math
{
public int add(int a, int b)
{
return a + b;
}
public int subtract(int a, int b)
{
return a - b;
}
}public class MathImplTest
{
public void testAdd() throws Exception
{
Math m = new MathImpl();
assertEquals(5, m.add(2, 3));
}
public void testSubtract() throws Exception
{
Math m = new MathImpl();
assertEquals(-1, m.subtract(2, 3));
}
}Introducing Arquillian
Arquillian is the next-generation in-container integration testing framework. Allowing fully dependency-injected unit testing, even JPA, JSF, and Web Services. Arquillian can be used to test nearly every Java EE component, but for the purpose of this document, we’ll just show you the most common case – how to test managed beans.Write an Arquillian Unit/Integration Test
Arquillian is where unit testing starts to get a little more exciting. This is where we actually test managed beans provided through dependency injection. While still using JUnit as the core testing framework, Arquillian tests a wider scope of the system, typically, than a pure JUnit test would; these are sometimes referred to as integration unit tests, or integration tests.Set up your Maven pom.xml
Add the following dependencies to your Maven pom.xml (or get started faster with Forge):<properties>
<version.arquillian>1.0.2.Final</version.arquillian>
<version.arquillian.container>1.0.0.CR3</version.arquillian.container>
<version.junit>4.10</version.junit>
<version.specs>3.0.1.Final</version.specs>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>${version.arquillian}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.spec</groupId>
<artifactId>jboss-javaee-6.0</artifactId>
<version>${version.specs}</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${version.junit}</version>
<scope>test</scope>
</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-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.5.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>
</dependencies>Your first Arquillian JUnit test-case:
Here we see the same unit test from above, but this time, using Arquillian to provide injection into the test case. This allows us to test components with many levels of dependency injection, or any other feature in CDI.@RunWith(Arquillian.class)
public class MathImplTest
{
/**
* Since Arquillian actually creates JAR files under the covers, the @Deployment
* is your way of controlling what is included in that Archive. Note, each
* class utilized in your test case - whether directly or indirectly - must be
* added to the deployment archive.
*/
@Deployment
public static JavaArchive createDeployment()
{
return ShrinkWrap.create(JavaArchive.class)
.addClass(Math.class).addClass(MathImpl.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
// Arquillian enables @Inject directly in the test case class itself!
@Inject Math m;
public void testAdd() throws Exception
{
assertEquals(5, m.add(2, 3));
}
public void testSubtract() throws Exception
{
assertEquals(-1, m.subtract(2, 3));
}
}Math is so simple, why did we bother to use Arquillian to test it?
Why/When to use Arquillian?
The answer is simple: In the above example, you probably wouldn’t have needed to use Arquillian, you could have just stuck with JUnit, but as soon as you have a situation like the one below, you might decide that it’s time for some extra power; for example, if you wanted to test a decorator. (Decorators extend the functionality of an existing interface without modifying the existing implementation.)public class MathDecorator implements Math
{
@Inject Math delegate;
@Inject User user;
@Inject Logger log;
public int add(int a, int b)
{
log.trace("Add was invoked by: " + user);
return delegate.add(a, b);
}
public int subtract(int a, int b)
{
log.trace("Subtract was invoked by: " + user);
return delegate.subtract(a, b);
}
}@RunWith(Arquillian.class)
public class MathDecoratorTest
{
/**
* Note in this example, we must add content to "beans.xml" in order to enable
* our decorator in CDI/Weld -- this is done in the deployment using the syntax below:
*/
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap.create(JavaArchive.class)
.addClasses(Math.class, MathImpl.class,
MathDecorator.class, MockUser.class, MockLogger.class)
.addAsManifestResource(
new StringAsset("<decorators><class>com.test.MathDecorator<class></decorators>"), "beans.xml");
}
// Arquillian enables @Inject directly in the test case class itself!
@Inject Math m;
@Inject MockLogger log;
@Inject User u;
public void testAdd() throws Exception
{
m.add(2, 3);
assertTrue(log.logged("Add was invoked by: " + u);
}
public void testSubtract() throws Exception
{
m.subtract(2, 3);
assertTrue(log.logged("Subtract was invoked by: " + u);
}
}Final Thoughts
There are many users of Arquillian: Apache DeltaSpike, JBoss AS7+, JBoss Forge, OCPSoft Rewrite and PrettyFaces, and more. So be sure to check out Arquillian — the next-generation in-container unit and integration testing suite!
About the author:
Lincoln Baxter, III is a Senior Software Engineer at Red Hat, working on JBoss open-source projects; most notably as project lead for JBoss Forge. This blog represents his personal thoughts and perspectives, not necessarily those of his employer.
He is a founder of OCPsoft, the author of PrettyFaces and Rewrite, the leading URL-rewriting extensions for Servlet, Java EE, and Java web frameworks; he is also the author of PrettyTime, social-style date and timestamp formatting for Java. When he is not swimming, running, or playing Ultimate Frisbee, Lincoln is focused on promoting open-source software and making web-applications more accessible for small businesses, individuals.