Testable development of CDI

Tutorial - Arquillian makes testing a breeze - Part 2

  

Run the Arquillian Test

Once you add all the necessary Arquillian libraries to the classpath, you can run an Arquillian test just like a unit test, whether you are running it from the IDE, the build script or any other test plugin. Let’s run the test in Eclipse.

From the IDE window, right click on the GreeterTest.java file in the Package Explorer (or in the editor) and select Run As | JUnit Test from the context menu (see fig. 3)

Fig.3: Starting the test

When you run the test, you should see the following lines printed to the console:

26 [main] INFO org.jboss.weld.Version – WELD-000900 1.1.1 (Final)

Hello, Earthling! You should then see the JUnit view appear, revealing a green bar (fig. 4)!

Fig.4: JUnit view revealing a green bar

You can also run the test on the commandline using Maven:

$ mvn test

You should see the following lines printed to the console:

-------------------------------------------------------

T E S T S

-------------------------------------------------------

Running org.arquillian.example.GreeterTest

21 [main] INFO org.jboss.weld.Version - WELD-000900 1.1.1 (Final)

Hello, Earthling!

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.858 sec

Congratulations! You’ve earned your first green bar with Arquillian!

A Closer Look

How do you know that CDI really worked? For all you know, Arquillian created a new instance of theGreeter class and injected it into the test without any involvement from CDI. Let’s prove it’s there. Create a new CDI bean named PhraseBuilder in the org.arquillian.example package that can create phrases from templates.

Listing 8

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

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;

public class PhraseBuilder {
   private Map<String, String> templates;

   public String buildPhrase(String id, Object... args) {
       return MessageFormat.format(templates.get(id), args);
   }

   @PostConstruct
   public void initialize() {
       templates = new HashMap<String, String>();
       templates.put("hello", "Hello, {0}!");
   }
}

Next, open up the Greeter class and create a new constructor that will inject PhraseBuilder using constructor injection. Then, delegate the task of creating the greeting to the injected bean (listing 9)

Listing 9

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

import java.io.PrintStream;
import javax.inject.Inject;

public class Greeter {

   private PhraseBuilder phraseBuilder;

   @Inject
   public Greeter(PhraseBuilder phraseBuilder) {
       this.phraseBuilder = phraseBuilder;
   }

   public void greet(PrintStream to, String name) {
       to.println(createGreeting(name));
   }

   public String createGreeting(String name) {
       return phraseBuilder.buildPhrase("hello", name);
   }
}

Now, in order for the test to work, an instance of PhraseBuilder must be created, its @PostConstructmethod invoked and must be injected into the constructor of Greeter when an instance of Greeter is created. We can be certain CDI is at work if all that comes together.

One last step. Because we created a new class, we must be sure that it’s getting added to the archive returned by the @Deployment method in the test. Simply change the line:

.addClass(Greeter.class)
...to read:
.addClasses(Greeter.class, PhraseBuilder.class)

Run the test again. You should get another green bar! Feels good, doesn’t it?

Debug the Test

This is going to be a short chapter. Why? Because an Arquillian test is so straightforward that you debug it exactly how you debug a unit test. Just add a breakpoint anywhere – either in the test or in the application code. Then right-click on the file and select Debug As | JUnit Test (figure 5).You’re now debugging in the container! Have fun poking around!

Fig.5: Debugging the container

If you are using a remote container, Debug As does not cause breakpoints to be activated. Instead, you need to start the container in debug mode and attach the debugger. That’s because the test is run in a different JVM than the original test runner.

As you’ve just witnessed, Arquillian is the ideal tool for testing CDI applications. It takes care of loading the CDI environment and injecting beans directly into the test. Best of all, when using an embedded CDI container, the test runs just as quickly as a unit test. If that’s all you need, then you can exit the tutorial now and start writing tests.

But! Is the embedded container telling the whole story? Will the component work if running inside a full container?

One of the perks of Arquillian is that you can run the same test in different compatible containers, whether it’s another embedded container or a standalone container. If you intend to use multiple containers, read on.

Add More Containers

As you learned earlier, Arquillian selects the container based on which container adapter is on the classpath. To switch to another container, you just change which container adapter is on the classpath before you run the test. There can only be one container adapter on the classpath at a given time.

One way to swap the libraries on the classpath is to manually edit the dependencies defined in thepom.xml each time. But that’s just crazy. There’s a much better way.

We can use Maven profiles to partition the dependencies into groups, one group for each container adapter and its related artifacts. When running the tests, you activate one of those groups to select the container using either a commandline flag (-P) or a preference in the IDE.

Open up the pom.xml and create a new profile for Weld EE embedded by inserting the following XML directly under the <dependencies> element (listing 10).

Listing 10

pom.xml
<!-- clip -->
<profiles>
   <profile>
       <id>arquillian-weld-ee-embedded</id>
       <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>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>
       </dependencies>
   </profile>
</profiles>
<!-- clip -->

Next, remove the jboss-javaee-6.0 dependency and the dependencies for the Weld EE embedded container adapter from the main <dependencies> section. Listing 11 shows how the <dependencies> and <profiles> sections should look when you’re done.

Listing 11

pom.xml
<!-- clip -->
<dependencies>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.8.1</version>
       <scope>test</scope>
   </dependency>
   <dependency>
       <groupId>org.jboss.arquillian.junit</groupId>
       <artifactId>arquillian-junit-container</artifactId>
       <scope>test</scope>
   </dependency>
</dependencies>
<profiles>
   <profile>
       <id>arquillian-weld-ee-embedded</id>
       <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>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>
       </dependencies>
   </profile>
</profiles>
<!-- clip -->

The Java EE API dependency has been moved to the profile since some containers, like Embedded GlassFish, already provide these libraries. Having both on the classpath at the same time would result in conflicts. So we have to play this classpath dance.

We’ll now include two additional profiles in the pom.xml inside the <profiles> element, the first for Embedded GlassFish (listing 12).

Listing 12

pom.xml
<!-- clip -->
<profile>
   <id>arquillian-glassfish-embedded</id>
   <dependencies>
       <dependency>
           <groupId>org.jboss.arquillian.container</groupId>
           <artifactId>arquillian-glassfish-embedded-3.1</artifactId>
           <version>1.0.0.CR2</version>
           <scope>test</scope>
       </dependency>
       <dependency>
           <groupId>org.glassfish.extras</groupId>
           <artifactId>glassfish-embedded-all</artifactId>
           <version>3.1</version>
           <scope>provided</scope>
       </dependency>
   </dependencies>
</profile>
<!-- clip -->

and the other for JBoss AS managed:

pom.xml
<!-- clip -->
<profile>
   <id>arquillian-jbossas-managed</id>
   <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>org.jboss.as</groupId>
           <artifactId>jboss-as-arquillian-container-managed</artifactId>
           <version>7.1.0.Final</version>
           <scope>test</scope>
       </dependency>
   </dependencies>
</profile>
<!-- clip -->

Now you have the choice of running the tests in one of three containers. If you’re having trouble with the pom.xml up to this point, you can download the file from the gist.

Test Across Containers

When you refresh the project in Eclipse, you’ll notice that it no longer builds. That’s because you need to activate one of the container profiles. Let’s activate the Weld EE embedded profile to restore the previous state. There are two ways to activate a Maven profile in Eclipse:

  1. Manual configuration (standard approach)
  2. Maven profile selector (JBoss Tools)

Set active Maven profile: Manual configuration

To set the active profile manually, follow these steps:

  1. Right click on the project and select Properties
  2.  Select the Maven properties tab
  3. Enter the profile id in the Active Maven Profiles field (e.g., arquillian-weld-ee-embedded)
  4. Click the OK button and accept the project changes

Figure 6 displays the Maven properties screen showing the profile we’ve activated:

Set active Maven profile: Maven profile selector

If you have JBoss Tools installed, selecting the active profile becomes much easier:

  1. Right click on the project and select Maven > Select Active Profiles…
  2. (alternatively, you can use the keybinding Ctrl+Shift+P or the button in the toolbar)
  3. Check the box next to the profile you want to activate (e.g., arquillian-weld-ee-embedded)
  4. Click the OK button

Figure 7 displays the Maven profile selector dialog showing the profile we’ve activated:

Once you have activated the profile, you should be able to run the test again successfully.

You already know the test works in Weld EE Embedded. Let’s switch to GlassFish Embedded by repeating the steps above, this time activating only the arquillian-glassfish-embedded profile. Run the test again. You should see GlassFish start in the console…and another green bar!

You’ve now run the same test on two different embedded containers, a CDI container (Weld) and a Java EE container (GlassFish). Both of these executions are in process. To really be sure the component works in a pure environment, we need to use a standalone container. Let’s switch to using JBoss AS.

To run the test on a standalone instance of JBoss AS, you first need to set it up. You can either:

  1. download and unpack it in a location outside the project or
  2. you can have Maven download and unpack it during a build.

Follow these steps to setup JBoss AS 7 outside the project:

  1. Download JBoss AS 7 
  2. (be sure the version you select matches the version you’ve defined in your pom.xml for <artifactId>jboss-as-arquillian-container-managed</artifactId>)
  3. Extract the archive
  4. (optional) Set the JBOSS_HOME environment variable to the path of the extracted directory

To have Maven handle this task for you instead, add the following XML fragment under the <id> element of the arq-jbossas-managed profile (listing 13).

Listing 13

pom.xml
<!-- clip -->
<build>
   <plugins>
       <plugin>
           <artifactId>maven-dependency-plugin</artifactId>
           <executions>
               <execution>
                   <id>unpack</id>
                   <phase>process-test-classes</phase>
                   <goals>
                       <goal>unpack</goal>
                   </goals>
                   <configuration>
                       <artifactItems>
                           <artifactItem>
                               <groupId>org.jboss.as</groupId>
                               <artifactId>jboss-as-dist</artifactId>
                               <version>7.1.0.Final</version>
                               <type>zip</type>
                               <overWrite>false</overWrite>
                               <outputDirectory>target</outputDirectory>
                           </artifactItem>
                       </artifactItems>
                   </configuration>
               </execution>
           </executions>
       </plugin>
   </plugins>
</build>
<!-- clip -->

To target a managed JBoss AS 7 instance, you also need a small bit of Arquillian configuration. Create the following configuration file and assign the value of the jbossHome property to the location where JBoss AS 7 is installed. If you’re using the Maven dependency plug-in, the location istarget/jboss-as-7.0.2.Final (listing 14).

Listing 14

src/test/resources/arquillian.xml
<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="jbossas-7-managed" default="true">
       <configuration>
           <property name="jbossHome">target/jboss-as-7.1.0.Final</property>
       </configuration>
   </container>
</arquillian>

Now change the active Maven profile to arquillian-jbossas-managed, then run the test again. You should see JBoss AS starting in the console…and yet another green bar!

The message printed to System.out gets written to the server log instead of the console.

That’s the same test, this time running in a standard (non-embedded) Java EE container. Arquillian packages the test, deploys to the container as a Java EE archive, executes the tests remotely, captures the results and feeds them back to the Eclipse JUnit result view (or in the Maven surefire results). You can read more about how this lifecycle works in the Arquillian reference guide.

Dan Allen, Andrew Rubinger, Aslak Knutsen

What do you think?

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

Comments

Latest opinions