August 17th, 2012 by Lincoln Baxter III

CDI-powered Unit Testing using Arquillian

When developing any system or software, it is important to test as much of that system as possible. Web frameworks are no exception; comprehensive, well-designed unit tests are critical for long-term success and maintenance. With the introduction of Contexts and Dependency Injection into the Java Enterprise framework (otherwise known as CDI – Weld, or Apache OpenWebBeans,) unit testing is as important as ever, but it would be nice to harness the power of dependency injection for use in unit tests, as well as in the production system!

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.

pom.xml
<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>
JUnit is the Java industry-standard unit-testing framework, and is one of the frameworks used in Apache Deltaspike; this should be your default test-framework.

Write your first plain JUnit test:

Tests are run automatically during a Maven build, such as mvn 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.
Math.java
public interface Math
{
   public int add(int a, int b);
   public int subtract(int a, int b);
}
MathImpl.java
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;
   }
}
Now that we have our Interface and Implementation, we can start to verify our functionality.
The unit test case
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));
   }
}
Simple enough, pretty slick, but now…

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):
pom.xml
<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.
MathImplTest.java
@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));
   }

}
Now that’s power and simplicity! Explicit control over which classes are even loaded into the container; however, you might say, 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.)
MathDecorator.java
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);
   }
}
Now in order to test the functionality here using only JUnit, you would need to create, instantiate, and set mocks for User and Logger, instantiate the decorator, then execute the test code. That’s a lot of work that you don’t really need to worry about if using Arquillian. For instance, this is what the Arquillian test case would look like:
MathDecoratorTest.java
@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);
   }

}
Pretty simple! No more creating chains of mocks — just make sure all the right classes are included in the deployment, and you’re good to go, test just as you would write code in the system itself. Arquillian will start up CDI and perform all the Dependency injection and container functions for you!

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!

Posted in JBoss

16 Comments

  1. Johan Eltes says:

    A typical application would involve multiple archives (libraries of CDI beans). I see a risk that the configuration defined by the @Deployement producer may not be the same as what I’m actually intend to test. Test works, but when running in container, a class is missing from an archive, although on the classpath (e.g. in a jar without a bean.xml). I guess it boils down to how you scope the definition of integration test. How to test modularized (in the sense of injection across archives) systems?

  2. Alberto Gori says:

    Is possible to use Arquillian to run tests in a real JSF environment?
    Something like JSFUnit or Seam Test (available in Seam 2).

    1. I would recommend looking at Arquillian Warp, it mixes client requests (jsf) and server side assertions in a new manner.

      https://github.com/arquillian/arquillian-extension-warp

  3. D0uD says:
    // Arquillian enables @Inject directly in the test case class itself!
       @Inject Math m;
       @Inject MockLogger log;
       @Inject User u;  // Why MockLogger and not MockUser here ?
    1. Lincoln says:

      Cood catch. This should simply be Logger! But it doesn’t really matter either way for the purposes of the test.

  4. Rodney says:

    You can find an example of JSFUnit w/ Arquillian here:

    http://anonsvn.jboss.org/repos/jsfunit/trunk/jboss-jsfunit-examples/arquillian/

    and the forum conversation about it here:

    http://community.jboss.org/thread/157418?start=0&tstart=0

  5. There’s also an example of testing w/Arquillian and JSFUnit here in the prettyfaces source:

    http://code.google.com/p/prettyfaces/source/browse/prettyfaces/trunk/tests-jsf2/

  6. […] they also support remote containers… interesting! Here’s another nice usecase of it: http://ocpsoft.com/seam/cdi-powered-unit-testing-using-arquillian/ The presentation was really well prepared and most probably the […]

  7. Tony Herstell says:

    Be wary as Arquillian does not support Conversation scope out of the box.

    1. I don’t exactly agree 🙂 arquillian supports whatever your container supports. So if your container supports conversations… so will arquillian.

      In fact, we test conversations using arquillian in a number of places. Seam faces, deltaspike, jboss as7 itself, the cdi tick 🙂

  8. johnny says:

    After building maven I’ve got issue: "The POM for xalan:xalan:jar:2.7.1.jbossorg-2 is missing, no dependency information available"

    I had to add to pom.xml

    <dependency>
    <groupId>org.apache.xalan</groupId>
    <artifactId>xalan</artifactId>
    <version>2.7.1-1.jbossorg</version>
    </dependency>

    and to get this dep I had to add also this

    <repositories>
    <repository>
    <id>thirdparty-releases</id>
    <name>JBoss Thirdparty Releases</name>
    <url>https://repository.jboss.org/nexus/content/repositories/thirdparty-releases</url&gt;
    </repository>
    </repositories>

    1. Oy. It looks like they still haven’t fixed that Xalan dependency bug 🙁 thanks for pointing this out!

  9. Asturiano says:

    You can fix it adding an exclusion to jboss-javaee-6.0 artifact

    <dependency>
          <groupId>org.jboss.spec</groupId>
           <artifactId>jboss-javaee-6.0</artifactId>
           <version>3.0.0.Final</version>
           <type>pom</type>
            <scope>provided</scope>
              <exclusions>
                  <exclusion>
                        <groupId>xalan</groupId>
                        <artifactId>xalan</artifactId>
                  </exclusion>
              </exclusions>
    </dependency>
  10. DR says:

    Just one thing. Arquillian is not for unit tests, but for integration tests. When you’re using real services, you’re testing those services as well as your code. It’s simply not a good idea to use something like Arquillian for unit tests, period.

    1. You are correct that you are testing the services as well as your code, but if you need to write mocks in order to “unit test” your code without Arquillian, then you are essentially testing your mocks, which are just as error prone as the services provided by the container via Arquillian.

      There is no such thing as a unit test 🙂

  11. Roy says:

    Thanks for post. Just update the beans.xml:

    new StringAsset("<beans><decorators><class>com.test.MathDecorator</class></decorators></beans>"), "beans.xml");

Leave a Comment




Please note: In order to submit code or special characters, wrap it in

[code lang="xml"][/code]
(for your language) - or your tags will be eaten.

Please note: Comment moderation is enabled and may delay your comment from appearing. There is no need to resubmit your comment.