Arquillian tests with an embedded Tomcat 7, CDI and Maven

20.10.2013

Tests with Arquillian, Tomcat and Dependency Injection

This short article describes how to setup the environment to test CDI (Dependency Injection) with Arquillian and Maven in an embedded Tomcat container.

The great article Arqullian - Getting Started describes how to setup the environment for an embedded Glasfish, a managed JBoss and an embedded Weld. But the description for Tomcat is missing.

It takes me a while until my environment was up and running, so I decided to write this short article. The setup is not very complicated, but you have to add some lines and commands at the correct position. :-)

This article assumes the you have the Greeter example of the article Arqullian - Getting Started in your workspace.

Extend the pom.xml

First of all you have to add a new Profile for the embedded Tomcat in the pom.xml.

<!-- Tomcat Embedded -->
<profile>
	<id>arquillian-tomcat-embedded</id>
	<activation>
		<activeByDefault>true</activeByDefault>
	</activation>
	<dependencies>
		<dependency>
			<groupId>org.jboss.arquillian.container</groupId>
			<artifactId>arquillian-tomcat-embedded-7</artifactId>
			<version>1.0.0.CR5</version>
			<scope>test</scope>
		</dependency>

		<!-- org.apache.tomcat -->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-core</artifactId>
			<version>7.0.42</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
			<version>7.0.42</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-logging-juli</artifactId>
			<version>7.0.42</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.eclipse.jdt.core.compiler</groupId>
			<artifactId>ecj</artifactId>
			<version>3.7</version>
			<scope>provided</scope>
		</dependency>

		<!-- Weld for Dependency Injection @Inject -->
		<dependency>
			<groupId>org.jboss.weld.servlet</groupId>
			<artifactId>weld-servlet</artifactId>
			<version>1.1.9.Final</version>
		</dependency>

		<dependency>
			<groupId>org.jboss.shrinkwrap.resolver</groupId>
			<artifactId>shrinkwrap-resolver-impl-maven</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
</profile>

Line 8 to 13 adds the embedded container for the Tomcat 7 and the lines 16 to 39 are needed for the Tomcat runtime. (see Tomcat 7.0 - Embedded)

Important are the lines 42-46. There you add the Weld-Servlet which is needed to setup the CDI for testing.

The ShrinkWrap-Resolver in line 48-52 is needed to add some dependencies in the class GreeterTest.

Create a arquillian.xml file

Now you have to create a configuration file for Arquillian (arquillian.xml).

Hint

Contrary to the article Tomcat 7.0 - Embedded I didn't need the file arquillian.xml with the unpackArchive property. I guess the newer version of Weld is able to find the classes of the CDI althought they a packed in the war file.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://jboss.org/schema/arquillian"
	xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">

	<container qualifier="tomcat" default="true">
		<configuration>
			<property name="tomcatHome">target/tomcat-embedded-7</property>
			<property name="workDir">work</property>
			<property name="bindHttpPort">8888</property>
			<property name="unpackArchive">true</property>
			<property name="serverName">arquillian-tomcat-embedded-7</property>
		</configuration>
	</container>
</arquillian>

Important is line 11 where you tell Tomcat to unzip the war files so Weld is able to find the classes. (see Tomcat 7.0 - Embedded)

Create a web.xml

In order to initialize the Weld servlet a web.xml file is needed.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

   <listener>
      <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
   </listener>
</web-app>

You only have to register the listener for the Weld environment.

Change the GreeterTest class

Next you have to add some lines in the class GreeterTest.

package org.arquillian.example;

import java.io.File;

import javax.inject.Inject;

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.WebArchive;
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class GreeterTest {

	@Inject
	Greeter greeter;

	@Deployment
    public static WebArchive createDeployment() {
		File[] lib = Maven.resolver()
	            .resolve("org.jboss.weld.servlet:weld-servlet:1.1.9.Final")
	            .withTransitivity().as(File.class);

		WebArchive jar =  ShrinkWrap.create(WebArchive.class)
            .addClass(Greeter.class)
            .addClass(PhraseBuilder.class)
            .addAsManifestResource("arquillian.xml")
            .addAsLibraries(lib)
            .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
            .setWebXML("web.xml")
            ;

        System.out.println(jar.toString(true));

        return jar;
    }

    @Test
    public void should_create_greeting() {
    	Assert.assertEquals("Hello, Earthling!", greeter.createGreeting("Earthling"));
    	greeter.greet(System.out, "Earthling");
    }
}

The lines 25 to 27 are used to resolve the dependencies for Weld and store them in a File array. The libraries in the array are added in line 33 to the war file. In line 32 you add the arquillian.xml (see arquillian.xml). Also you have to add the web.xml file in line 35.

That's all. If you start the GreeterTest now, the embedded Tomcat is started and the injection of the @Inject Greeter greeter; is working.

Further informations about ShrinkWrap and the Maven Resolver can be found here: How to I add Maven artifacts to my ShrinkWrap archives?.

Arquillian test example for the arquillian-container-tomcat

On GitHub you can find an example with a configuration for the embedded Tomcat container. This example uses the org.jboss.shrinkwrap.resolver.api.DependencyResolvers and the org.jboss.shrinkwrap.resolver.api.maven.MavenDependencyResolver to resolve the dependencies for the Arquillian tests. (TomcatEmbeddedInContainerTestCase.java)

The problem is that the two classes are moved during a refactoring. :-) In the version 1.0.0 they are still available. In newer versions they are gone and you have to use another way to resolve the dependencies. How to I add Maven artifacts to my ShrinkWrap archives?

Some expections

In this section I summarized some exceptions I got when I tried to initialize the embedded Tomcat container. Perhaps these StackTraces help others to find their errors faster.

To fix the following exception, you have to add some lines in the pom.xml so that the Tomcat runtime is found. See the lines 15-39 in section Extend the pom.xml

Caused by: java.lang.NoClassDefFoundError: org/apache/catalina/Host
	at java.lang.Class.getDeclaredConstructors0(Native Method)
	at java.lang.Class.privateGetDeclaredConstructors(Class.java:2483)
	at java.lang.Class.getConstructor0(Class.java:2793)
	at java.lang.Class.getDeclaredConstructor(Class.java:2043)
	at org.jboss.arquillian.core.impl.loadable.SecurityActions$1.run(SecurityActions.java:182)
	at org.jboss.arquillian.core.impl.loadable.SecurityActions$1.run(SecurityActions.java:179)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.jboss.arquillian.core.impl.loadable.SecurityActions.getConstructor(SecurityActions.java:178)
	at org.jboss.arquillian.core.impl.loadable.SecurityActions.newInstance(SecurityActions.java:152)
	at org.jboss.arquillian.core.impl.loadable.ServiceRegistryLoader.createServiceInstance(ServiceRegistryLoader.java:103)
	at org.jboss.arquillian.core.impl.loadable.ServiceRegistryLoader.all(ServiceRegistryLoader.java:55)
	at org.jboss.arquillian.core.impl.loadable.ServiceRegistryLoader.onlyOne(ServiceRegistryLoader.java:67)
	at org.jboss.arquillian.container.impl.client.container.ContainerRegistryCreator.createRegistry(ContainerRegistryCreator.java:97)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
	at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99)
	at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81)
	at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135)
	at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115)
	at org.jboss.arquillian.core.impl.ManagerImpl.bindAndFire(ManagerImpl.java:236)
	at org.jboss.arquillian.core.impl.InstanceImpl.set(InstanceImpl.java:74)
	at org.jboss.arquillian.config.impl.extension.ConfigurationRegistrar.loadConfiguration(ConfigurationRegistrar.java:60)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
	at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99)
	at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81)
	at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135)
	at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115)
	at org.jboss.arquillian.core.impl.ManagerImpl.start(ManagerImpl.java:261)
	at org.jboss.arquillian.test.impl.EventTestRunnerAdaptor.(EventTestRunnerAdaptor.java:56)
	... 15 more
Caused by: java.lang.ClassNotFoundException: org.apache.catalina.Host
	at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 51 more

If you got a NullPointerException when running the JUnit test the CDI is not initialized. I describe how to solve it above.

java.lang.NullPointerException
	at org.arquillian.example.GreeterTest.should_create_greeting(GreeterTest.java:32)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
	at org.jboss.arquillian.junit.Arquillian$6$1.invoke(Arquillian.java:270)
	at org.jboss.arquillian.container.test.impl.execution.LocalTestExecuter.execute(LocalTestExecuter.java:60)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
	at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99)
	at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81)
	at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135)
	at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115)
	at org.jboss.arquillian.core.impl.EventImpl.fire(EventImpl.java:67)
	at org.jboss.arquillian.container.test.impl.execution.ContainerTestExecuter.execute(ContainerTestExecuter.java:38)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
	at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99)
	at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81)
	at org.jboss.arquillian.test.impl.TestContextHandler.createTestContext(TestContextHandler.java:89)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
	at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88)
	at org.jboss.arquillian.test.impl.TestContextHandler.createClassContext(TestContextHandler.java:75)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
	at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88)
	at org.jboss.arquillian.test.impl.TestContextHandler.createSuiteContext(TestContextHandler.java:60)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94)
	at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88)
	at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135)
	at org.jboss.arquillian.test.impl.EventTestRunnerAdaptor.test(EventTestRunnerAdaptor.java:111)
	at org.jboss.arquillian.junit.Arquillian$6.evaluate(Arquillian.java:263)
	at org.jboss.arquillian.junit.Arquillian$4.evaluate(Arquillian.java:226)
	at org.jboss.arquillian.junit.Arquillian.multiExecute(Arquillian.java:314)
	at org.jboss.arquillian.junit.Arquillian.access$100(Arquillian.java:46)
	at org.jboss.arquillian.junit.Arquillian$5.evaluate(Arquillian.java:240)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
	at org.jboss.arquillian.junit.Arquillian$2.evaluate(Arquillian.java:185)
	at org.jboss.arquillian.junit.Arquillian.multiExecute(Arquillian.java:314)
	at org.jboss.arquillian.junit.Arquillian.access$100(Arquillian.java:46)
	at org.jboss.arquillian.junit.Arquillian$3.evaluate(Arquillian.java:199)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
	at org.jboss.arquillian.junit.Arquillian.run(Arquillian.java:147)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
	at org.jboss.arquillian.junit.container.JUnitTestRunner.execute(JUnitTestRunner.java:65)
	at org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner.executeTest(ServletTestRunner.java:160)
	at org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner.execute(ServletTestRunner.java:126)
	at org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner.doGet(ServletTestRunner.java:90)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1002)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:724)

The following entries in the logfile are a hint that Weld is not initialized properly and the file web.xml is missing.

INFO: No global web.xml found
Okt 20, 2013 6:48:30 PM org.jboss.arquillian.testenricher.cdi.container.BeanManagerProducer lookup
INFO: BeanManager not found.