Saturday 21 January 2012

Unit tests with embeded tomcat artifacts

In the 7.x releases of Apache Tomcat, some maven artifacts are now published which include a nice and fluent embeded api to run a Tomcat instance.

So it's a nice opportunity to use it writing units to test servlets, rest api etc..
But until 7.0.25 it was only possible to do it with using a barcoding port which can cause some issues on ci servers where you are not sure ports are not used by something else running.

I have personally sended a RFC to ITEF to have port allocation for only my personal use on my birthday year or zip code port but strangely this RFC was never approved :-).

Now you can use the java ServerSocket port 0 feature to use any free port available on the machine.
It has been fixed with the issue 52028.

So now you can write a unit test as it (here a test with a REST service provided by Apache CXF).



@Before
public void startTomcat()
throws Exception
{
tomcat = new Tomcat();
tomcat.setBaseDir( System.getProperty( "java.io.tmpdir" ) );
tomcat.setPort( 0 );

Context context = tomcat.addContext( "", System.getProperty( "java.io.tmpdir" ) );

A context param in your web.xml:

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/spring-context.xml</param-value>
</context-param>

In the code

ApplicationParameter applicationParameter = new ApplicationParameter();
applicationParameter.setName( "contextConfigLocation" );
applicationParameter.setValue( "classpath*:META-INF/spring-context.xml" );
context.addApplicationParameter( applicationParameter );

A listener class in your web.xml:

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

In the code

context.addApplicationListener( ContextLoaderListener.class.getName() );

CXF servlet declaration in your web.xml:

<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/restServices/*</url-pattern>
</servlet-mapping>

In the code:

tomcat.addServlet( context, "cxf", new CXFServlet() );
context.addServletMapping( "/restServices/*", "cxf" );

tomcat.start();

port = tomcat.getConnector().getLocalPort();

System.out.println("Tomcat started on port:"+port);
}



So now you can test/consume you REST services on localhost with the port.

Don't miss to shutdown the tomcat instance on tearDown or @After

@After
public void stopTomcat()
throws Exception
{
tomcat.stop();
}


If you use Maven you need the following dependencies:

<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<scope>test</scope>
<version>7.0.25</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-juli</artifactId>
<scope>test</scope>
<version>7.0.25</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
<scope>test</scope>
<version>7.0.25</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<scope>test</scope>
<version>7.0.25</version>
</dependency>


As samples talks more than long docs ("Code talks, bullshit walks" :-) ).
The tomcat maven archetype has been improved with a sample. (see previous post)

Have Fun!

2 comments:

Anonymous said...

Hi Olamy,

the blogpost contain a typo. Close your maven version tags ;)

olamy said...

fixed.
Thanks !