Java EE 6 Testing Part II – Introduction to Arquillian and ShrinkWrap
This post focus on Arquillian and ShrinkWrap and why they are awesome tools for integration testing of enterprise Java applications.
The source code used for this post is available on GitHub under the folder arquillian-shrinkwrap
.
The tools
- Arquillian
- Arquillian brings test execution to the target runtime, alleviating the burden on the developer of managing the runtime from within the test or project build. To invert this control, Arquillian wraps a lifecycle around test execution that does the following:
- Manages the lifecycle of one or more containers
- Bundles the test case, dependent classes and resources as ShrinkWrap archives
- Deploys the archives to the containers
- Enriches the test case with dependency injection and other declarative services
- Executes the tests inside (or against) the containers
- Returns the results to the test runner for reporting
- ShrinkWrap
ShrinkWrap, a central component of Arquillian, provides a simple mechanism to assemble archives like JARs, WARs, and EARs with a friendly, fluent API.
One of the major benefits of using Arquillian is that you run the tests in a remote container (i.e. application server). That means you’ll be testing the real deal. No mocks. Not even embedded runtimes!
Agenda
The following topics will be covered on this post:
- Configure the Arquillian infrastructure in a Maven-based Java project
- Inject EJBs and Managed Beans (CDI) directly in test instances
- Test Java Persistence API (JPA) layer
- Run Arquillian in client mode
- Run and debug Arquillian tests inside your IDE
Configure Maven to run integration tests
To run integration tests with Maven we need a different approach. By different approach I mean a different plugin: the Maven Failsafe Plugin.
The Failsafe Plugin is a fork of the Maven Surefire Plugin designed to run integration tests.
The Failsafe plugin goals are designed to run after the package phase, on the integration-test phase.
The Maven lifecycle has four phases for running integration tests:
- pre-integration-test: on this phase we can start any required service or do any action, like starting a database, or starting a webserver, anything…
- integration-test: failsafe will run the test on this phase, so after all required services are started.
- post-integration-test: time to shutdown all services…
- verify: failsafe runs another goal that interprets the results of tests here, if any tests didn’t pass failsafe will display the results and exit the build.
Configuring Failsafe in the POM:
<!-- clip --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.12</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>2.12</version> <configuration> <encoding>UTF-8</encoding> </configuration> <executions> <execution> <id>integration-test</id> <goals> <goal>integration-test</goal> </goals> </execution> <execution> <id>verify</id> <goals> <goal>verify</goal> </goals> </execution> </executions> </plugin> <!-- clip -->
By default, the Surefire plugin executes **/Test*.java
, **/*Test.java
, and **/*TestCase.java
test classes. The Failsafe plugin will look for **/IT*.java
, **/*IT.java
, and **/*ITCase.java
. If you are using both the Surefire and Failsafe plugins, make sure that you use this naming convention to make it easier to identify which tests are being executed by which plugin.
Configure Arquillian infrastructure in Maven
Configure your Maven project descriptor to use Arquillian by appending the following XML fragment:
<!-- clip --> <repositories> <repository> <id>jboss-public-repository-group</id> <name>JBoss Public Repository Group</name> <url>http://repository.jboss.org/nexus/content/groups/public/</url> </repository> </repositories> <dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.arquillian</groupId> <artifactId>arquillian-bom</artifactId> <version>1.0.1.Final</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.arquillian.testng</groupId> <artifactId>arquillian-testng-container</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.4</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>3.0.1.Final</version> <scope>provided</scope> <type>pom</type> </dependency> </dependencies> <profiles> <profile> <id>jbossas-remote-7</id> <activation> <activeByDefault>true</activeByDefault> </activation> <dependencies> <dependency> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-arquillian-container-remote</artifactId> <version>7.1.1.Final</version> <scope>test</scope> </dependency> </dependencies> </profile> </profiles> <!-- clip -->
Arquillian has a vast list of container adapters. An Arquillian test can be executed in any container that is compatible with the programming model used in the test. However, throughout this post, only JBoss AS 7 is used.
Similarly to Java EE 6 Testing Part I, I chose to use TestNG testing framework, but again, JUnit should work just as well.
Create testable components
Before looking at how to write integration tests with Arquillian we first need to have a component to test.
A Session Bean is a common component in Java EE stack and will serve as test subject. In this post, I’ll be creating a very basic backend for adding new users to a database.
@Stateless public class UserServiceBean { @PersistenceContext private EntityManager em; public User addUser(User user) { em.persist(user); return user; } // Annotation says that we do not need to open a transaction @TransactionAttribute(TransactionAttributeType.SUPPORTS) public User findUserById(Long id) { return em.find(User.class, id); } }
In the code above I use JPA and so we need a persistence unit.
A persistence unit defines a set of all entity classes that are managed by EntityManager
instances in an application. This set of entity classes represents the data contained within a single data store.
Persistence units are defined by the persistence.xml
configuration file:
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="example"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <property name="hibernate.hbm2ddl.auto" value="create-drop" /> <property name="hibernate.show_sql" value="true" /> </properties> </persistence-unit> </persistence>
In this example I’m using an example data source that uses H2 database and comes already configured with JBoss AS 7.
Finally, we also need an entity that maps to a table in the database:
@Entity public class User { @Id @GeneratedValue private Long id; @NotNull private String name; // Removed constructors, getters and setters for brevity @Override public String toString() { return "User [id=" + id + ", name=" + name + "]"; } }
Test JPA with Arquillian
We are now all set to write our first Arquillian test.
An Arquillian test case looks just like a unit test with some extras. It must have three things:
- Extend Arquillian class (this is specific to TestNG, with JUnit you need a
@RunWith(Arquillian.class)
annotation on the class) - A public static method annotated with @Deployment that returns a ShrinkWrap archive
- At least one method annotated with @Test
public class UserServiceBeanIT extends Arquillian { private static final Logger LOGGER = Logger.getLogger(UserServiceBeanIT.class.getName()); @Inject private UserServiceBean service; @Deployment public static JavaArchive createTestableDeployment() { final JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "example.jar") .addClasses(User.class, UserServiceBean.class) .addAsManifestResource("META-INF/persistence.xml", "persistence.xml") // Enable CDI .addAsManifestResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml")); LOGGER.info(jar.toString(Formatters.VERBOSE)); return jar; } @Test public void callServiceToAddNewUserToDB() { final User user = new User("Ike"); service.addUser(user); assertNotNull(user.getId(), "User id should not be null!"); } }
This test is straightforward, it inserts a new user and checks that the id
property has been filled with the generated value from the database.
Since the test is enriched by Arquillian, you can inject EJBs and managed beans normally using @EJB
or @Inject
annotations.
The method annotated with @Deployment
uses ShrinkWrap to build a JAR archive which will be deployed to the container and to which your tests will be run against. ShrinkWrap isolates the classes and resources which are needed by the test from the remainder of the classpath, you should include every component needed for the test to run inside the deployment archive.
Client mode
Arquillian supports three test run modes:
- In-container mode is to test your application internals. This gives Arquillian the ability to communicate with the test, enrich the test and run the test remotely. In this mode, the test executes in the remote container; Arquillian uses this mode by default.
- Client mode is to test how your application is used by clients. As opposed to in-container mode which repackages and overrides the test execution, the client mode does as little as possible. It does not repackage your
@Deployment
nor does it forward the test execution to a remote server. Your test case is running in your JVM as expected and you’re free to test the container from the outside, as your clients see it. The only thing Arquillian does is to control the lifecycle of your@Deployment
. - Mixed mode allows to mix the two run modes within the same test class.
To run Arquillian in client mode lets first build a servlet to be tested:
@WebServlet("/User") public class UserServlet extends HttpServlet { private static final long serialVersionUID = -7125652220750352874L; @Inject private UserServiceBean service; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain"); PrintWriter out = response.getWriter(); out.println(service.addUser(new User("Ike")).toString()); out.close(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
And now lets test it:
public class UserServletIT extends Arquillian { private static final Logger LOGGER = Logger.getLogger(UserServletIT.class.getName()); // Not managed, should be used for external calls (e.g. HTTP) @Deployment(testable = false) public static WebArchive createNotTestableDeployment() { final WebArchive war = ShrinkWrap.create(WebArchive.class, "example.war") .addClasses(User.class, UserServiceBean.class, UserServlet.class) .addAsResource("META-INF/persistence.xml") // Enable CDI .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml")); LOGGER.info(war.toString(Formatters.VERBOSE)); return war; } @RunAsClient // Same as @Deployment(testable = false), should only be used in mixed mode @Test(dataProvider = Arquillian.ARQUILLIAN_DATA_PROVIDER) public void callServletToAddNewUserToDB(@ArquillianResource URL baseURL) throws IOException { // Servlet is listening at <context_path>/User final URL url = new URL(baseURL, "User"); final User user = new User(1L, "Ike"); StringBuilder builder = new StringBuilder(); BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream())); String line; while ((line = reader.readLine()) != null) { builder.append(line); } reader.close(); assertEquals(builder.toString(), user.toString()); } }
Although this test is very simple, it allows you to test multiple layers of you application with a single method call.
Run tests inside Eclipse
You can run an Arquillian test from inside your IDE just like a unit test.
Run an Arquillian test
(Click on the images to enlarge)
- Install TestNG and JBoss Tools Eclipse plugins.
- Add a new JBoss AS server to Eclipse:
- Start JBoss AS server:
- Run the test case from Eclipse, right click on the test file on the Project Explorer and select
Run As > TestNG Test
:
The result should look similar to this:
Debug an Arquillian test
(Click on the images to enlarge)
Since we are using a remote container Debug As > TestNG Test
does not cause breakpoints to be activated.
Instead, we 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.
The only change you need to make to debug your test is to start JBoss AS server in debug mode:
- Start JBoss AS server debug mode:
- Add the breakpoints you need to your code.
- And debug it by right clicking on the test file on the Project Explorer and selecting
Run As > TestNG Test
:
More resources
I hope to have been able to highlight some of the benefits of Arquillian.
For more Arquillian awesomeness take a look at the following resources:
Related Posts
- Unit Testing JBoss 5 Services
- Java EE 6 Testing Part I – EJB 3.1 Embeddable API
- Maven 2 Cobertura Plugin – Updated
- JBoss PojoCache configuration
- JBoss AS 5.0 is out!
- Previous Entry: Java EE 6 Testing Part I – EJB 3.1 Embeddable API
- Next Entry: Comparing OpenDDR to WURFL
Reference: Java EE 6 Testing Part II – Introduction to Arquillian and ShrinkWrap from our JCG partner Samuel Santos at the Samaxes blog.