The Arquillian test framework

Arquillian is a new approach for writing Java EE testing. In the past developers and engineers have been used to writing mock objects and unit tests that run outside of the application container for sheer productivity, efficiency, and speed.

A new kind of Java EE testing framework

Arquillian is a framework that merges the functional and integration testing. It takes control of the lifecycle of the application server container so that it can provide a test execution in an easier manner to developers.

  • Arquillian manages the lifecycle of the target container, which can be a CDI or EE container.
  • It bundles the test case, dependencies, and resources into a much smaller distribution than usual, which it calls the ShrinkWrap archives.
  • Arquillian deploys the said ShrinkWrap archives to the runtime target container, and then proceeds to enrich the test case with dependency injection and other runtime declarative resources.
  • The unit test cases are executed inside the target container. The results of the unit tests are made available to the test runner.
  • After executing the test cases, Arquillian shuts down the target container; meanwhile, the unit test results are available for reporting.

Arquillian bundles up the unit test and dependencies into clever assemblies of class and deploys them to a CDI or Java EE container, and thus developers know that their code actually runs well inside the product, instead of guessing at the accuracy of mock implementation.

You can get more information about Arquillian from the the JBoss website http://www.jboss.org/arquillian.html. Most of the time, you probably do not want to manually download the Arquillian framework, instead you will probably integrate into a Maven build or for this written book, add it as the build dependencies for Gradle.

Setting up of Arquillian

Arquillian works with popular testing frameworks JUnit and TestNG out-of-the-box, and it also integrates into IDE such as IntelliJ IDEA, NetBeans, and Eclipse.

In this section, we will set up an Arquillian with the Gradle build tool. The following is the new build.gradle file with new dependencies on Gradle:

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'eclipse'
apply plugin: 'idea'

group = 'com.javaeehandbook.book1'
archivesBaseName = 'ch02-cdi-arquillian'
version = '1.0'

repositories {
  mavenCentral()
  maven {
    url 'http://repository.jboss.org/nexus/content/groups/public''
  }
}

dependencies {
  compile 'org.jboss.spec:jboss-javaee-6.0:1.0.0.Final'
  compile 'org.jboss.arquillian:arquillian-bom:1.0.3.Final'
  
  testCompile 'org.jboss.weld.se:weld-se-core:1.1.9.Final'
  testCompile 'org.slf4j:slf4j-simple:1.6.4'
  testCompile 'org.jboss.arquillian.container: arquillian-weld-ee-embedded-1.1:1.0.0.CR3'
  testCompile 'org.jboss.arquillian.junit: arquillian-junit-container:1.0.2.Final'
  testCompile 'junit:junit:4.11'
}

task wrapper(type: Wrapper) {
  gradleVersion = '1.7'
}

// Override Gradle defaults - a force an exploded JAR view
sourceSets {
  main {
    output.resourcesDir = 'build/classes/main'
    output.classesDir = 'build/classes/main'
  }
  test {
    output.resourcesDir = 'build/classes/test'
    output.classesDir = 'build/classes/test'
  }
}

The URL http://repository.jboss.org/nexus/content/groups/public is introduced as a second repository, in order to retrieve the correct dependencies from the JBoss Red Hat servers. Apart from this change and the extra dependencies, it looks very much the same.

Arquillian has two parts: core and the embedded container adaptors. There is core of the framework, the support classes, and one runner from JUnit or TestNG; and then there is the adaptor. In order to run successfully, at least one embedded container must be specified. In the unit test, here we are using the Weld Java EE embedded container, namely arquillian-weld-ee-embedded-1.1. However, you are free to choose another container adaptor, of which there are several, such as the JBoss application or the GlassFish servers.

Note

Arquillian even allows developers to test their execution against multiple containers, but only one can be selected for the runtime. The way to do test across many application servers is through configuring the Maven profiles or the Gradle setting properties. This is an advanced topic and details can be found online at http://gradle.org/docs/current/userguide/working_with_files.html.s

The disposable methods

Let us revisit the classes that we created with the @Produces annotation. Remember the annotation @Produces informs the CDI container to instantiate a bean type dynamically through an application client factory. Having created a bean type, we should consider end of life of factory bean types. What if the bean was expensive to create or if the bean had held on to the resource handle? How could we safely release such a resource to the operating system via the JVM, naturally? This is the purpose of the @Dispose annotation.

When a contextual bean goes out of scope, it is destroyed. To destroy a bean, the CDI container calls any @PreDestroy callbacks for the bean and destroys any @Dependent objects before disposing of the object.

An application can perform custom cleanup of the created objects by using a dispose method. Marking the parameter with the annotation @javax.enterprise.inject.Disposes designates it as a disposal method.

The revised code for the class HouseholdCredit is as follows:

public class HouseholdCredit {
  private static AtomicInteger counter = new AtomicInteger(1000);
  
  @Produces
  @Economy
  public CreditProcessor createCreditProcessor() {
    CreditProcessor proc = new StreetCreditProcessor("promoter"+counter.getAndIncrement());
    System.out.printf("#createCreditProcessor() "+ "creates proc = %s
", proc);
    return proc;
  }
  
  public void releaseCreditProcessor(@Disposes @Economy CreditProcessor proc) {
    System.out.printf("#releaseCreditProcessor() "+"dispose proc = %s
", proc);
  }
  
  public static class StreetCreditProcessor
  implements CreditProcessor {
    private final String workerId;
    public StreetCreditProcessor(String workerId) {
      this.workerId = workerId;
    }
    /*... same as before ... */
  }
}

Note that the disposal method accepts the same qualifier annotation as the production factory method. This is important; the CDI container should complain at runtime if the qualifiers and the conversational scope, if any, between the production and disposal methods do not match.

The modified unit test with the Arquillian framework to verify the operation of the HouseholdCredit bean is as follows:

package je7hb.basic.arquillian;
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.runner.RunWith;
import javax.inject.Inject;
import static org.junit.Assert.assertNotNull;

@RunWith(Arquillian.class)
public class EconomyCreditProcessorTest {
  @Deployment
  public static JavaArchive createDeployment() {
    JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
    .addClasses(Economy.class, Premium.class, CreditProcessor.class, HouseholdCredit.class, PremiumCreditProcessor.class)
    .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    System.out.println(jar.toString(true));
    return jar;
  }
  
  private @Inject @Economy CreditProcessor processor;
  
  @Test
  public void should_create_greeting() {
    System.out.printf("processor = %s
", processor);
    assertNotNull(processor);
    processor.check("1234");
  }
}

An Arquillian JUnit test case requires three important items: the @RunWith annotation with the Arquillian class reference, the deployment static method, which is annotated, and at least one method annotated with the @Test method.

The deployment method is annotated with @Deployment, which is part of the Arquillian testing framework, and is responsible for creating, the ShrinkWrap bundle. The ShrinkWrap API is very similar to the static builder factory. The object is instantiated and then properties are set on the builder through the add method. The classes to be deployed are explicitly added, and adding a blank CDI bean configuration file follows it. (Java Packages can also be specifically deployed.)

The output of running this unit test is as follows:

3d3e541b-c6b9-4e58-918d-febae05ead20.jar:
/je7hb/
/je7hb/basic/
/je7hb/basic/arquillian/
/je7hb/basic/arquillian/HouseholdCredit.class
/je7hb/basic/arquillian/PremiumCreditProcessor.class
/je7hb/basic/arquillian/HouseholdCredit$StreetCreditProcessor.class
/je7hb/basic/arquillian/Economy.class
/je7hb/basic/arquillian/CreditProcessor.class
/je7hb/basic/arquillian/Premium.class
/META-INF/
/META-INF/beans.xml
21 [main] INFO org.jboss.weld.Version - WELD-000900 1.1.9 (Final)
Household#createCreditProcessor() creates proc = StreetCreditProcessor{workerId='promoter1000'}
processor = StreetCreditProcessor{workerId = 'promoter1000'}
check for account [1234]
Household#releaseCreditProcessor() dispose proc = StreetCreditProcessor{workerId='promoter1000'}

The println() statement in the deployment static method is actually dumping the contents of the ShrinkWrap bundle. It can be useful to see this debuggable output in some certain situations, but for normal development work it will be annoying for other staff. So, I recommend that you don't forget to remove that debug line.

From the preceding screen output, we can see the Arquillian framework bundles up the classes into a bundle, performs the dependency execution, and executes the test. Most importantly, it handles the lifecycle of the container beans.

Moreover, because the test works in Weld, in the CDI container as it truly runs, there is really good confidence to assume that it work, as expected in the GlassFish and JBoss application server. After all, this is exactly all about the standards and portability of the code.

Let us move to the penultimate area of CDI for this overly long tutorial. How can the CDI container manage crosscutting concerns for managed beans?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.88.62