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.
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 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.
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.
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
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?
18.220.88.62