16 October 2014

Integration Testing for STS Extensions with Jetty

Recently I had to develop some extensions (ClaimHandler, Validator) to the CXF STS. My problem at first was, how to write an integration test that proves the correct implementation of my extensions.

At first I placed my Mockup classes and web config in the src/main folder from my maven project and added the jetty plugin to my pom.xml file. This way I was able to start my REST MockupService simply by typing mvn jetty:run on the console. After starting the service I was able to execute my test classes directly from Eclipse. But this approach did not satisfy me at all, because now I had lots of files in my main project folder, which would not be needed once I build and deploy my STS extensions to another STS installation. Somehow I needed to move all files (Mockup Service, Spring beans.xml, web.xml, etc.) to the test project folder.

In this post I'll explain how to setup you maven pom file so that you can use Jetty in your integration test phase if your packaging goal is not a war file but a simple jar file instead and all your web configuration and classes are located in your test folder.

There are two Blogs which I found very helpful to get my use case up and running:
  1. Run Jetty in the Maven life-cycle
  2. End-to-End Client-Server Integration Testing with Maven – Project Setup
First of all I moved my complete src/main/webapp folder to src/test/webapp as well as src/main/resources to src/test/resources since I needed these files only for testing. The same was true for some of my classes in src/main/java which I moved to src/test/java. Now my src/main/ folder only contained my Java classes which I needed to provide my new STS extension.

Next I modified my pom.xml file and changed the packaging from war to jar. My goal was to add this maven project later as a dependency to another project which would result as a lib in the STS war archive.

Allocating dynamic network ports to avoid test failures caused by port collisions

To get a dynamic port binding for my test services, I added a nice maven helper to my build allocating free ports for me:
<project>
    . . . 
    <build>
        . . . 
        <plugins>
            . . .
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>1.5</version>
                <executions>
                    <execution>
                        <id>reserve-network-port</id>
                        <goals>
                            <goal>reserve-network-port</goal>
                        </goals>
                        <phase>process-test-resources</phase>
                        <configuration>
                            <portNames>
                                <portName>jettyServerPort</portName>
                                <portName>jettyServerStopPort</portName>
                            </portNames>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        <plugins>
    <build>
<project>

To ensure that my test classes can know the correct location of my Mock Services I simply set these dynamic values as a system property, which can be picked up in my test class:
<project>
    . . . 
    <build>
        . . . 
        <plugins>
            . . .
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.8.1</version>
                <executions>
                    <execution>
                        <id>integration-test</id>
                        <goals>
                            <goal>integration-test</goal>
                        </goals>
                        <configuration>
                            <systemPropertyVariables>
                                <service.url>http://localhost:${jettyServerPort}/myRestService</service.url>
                                <sts.url>http://localhost:${jettyServerPort}/SecurityTokenService/UT</sts.url>
                            </systemPropertyVariables>
                        </configuration>
                    </execution>
                    <execution>
                        <id>verify</id>
                        <goals>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        <plugins>
    <build>
<project>

Jetty Plugin Configuration

And here comes the really interesting part of telling Jetty to run with classes and configuration files from the src/test project folder:
<project>
    . . . 
    <build>
        . . . 
        <plugins>
            . . .
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>8.1.5.v20120716</version>
                <configuration>
                    <systemProperties>
                        <systemProperty>
                            <name>java.security.auth.login.config</name>
                            <value>login.jaas</value>
                        </systemProperty>
                        <systemProperty>
                            <name>service.url</name>
                            <value>http://localhost:8080/myRestService</value>
                        </systemProperty>
                    </systemProperties>
                    <scanIntervalSeconds>5</scanIntervalSeconds>
                    <webAppConfig>
                        <resourceBases>
                            <resourceBase>${project.basedir}/src/test/webapp</resourceBase>
                        </resourceBases>
                    </webAppConfig>
                    <useTestScope>true</useTestScope>
                    <stopKey>STOP</stopKey>
                    <stopPort>${jettyServerStopPort}</stopPort>
                </configuration>
                <executions>
                    <execution>
                        <id>start-jetty</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>start</goal>
                        </goals>
                        <configuration>
                            <scanIntervalSeconds>0</scanIntervalSeconds>
                            <daemon>true</daemon>
                            <connectors>
                                <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
                                    <port>${jettyServerPort}</port>
                                    <maxIdleTime>60000</maxIdleTime>
                                </connector>
                            </connectors>
                            <systemProperties>
                                <systemProperty>
                                    <name>java.security.auth.login.config</name>
                                    <value>login.jaas</value>
                                </systemProperty>
                                <systemProperty>
                                    <name>service.url</name>
                                    <value>http://localhost:${jettyServerPort}/myRestService</value>
                                </systemProperty>
                            </systemProperties>
                        </configuration>
                    </execution>
                    <execution>
                        <id>stop-jetty</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        <plugins>
    <build>
<project>

I tried testing first with version 9.2.3.v20140905 of the Jetty plugin, but for some reasons I could not discover so quickly the SelectChannelConnector would not work as expected. My test would always run at port 8080 instead of a dynamically picked port. Therefore I switched back to version 8.1.5.v20120716 and all was running as desired.

The most important parts to mention here is to set the resourceBase in the configuration to ${project.basedir}/src/test/webapp as well as setting useTestScope to true.

Since I still wanted to use the port 8080 when running mvn jetty:run but a dynamic port when running mvn verify, I had to override the port settings accordingly.

After these changes I was able to build my extensions including the automated integration test, but without any test related code in my src/main/ folder.

4 comments: