September 15, 2010

Spring 3 + maven2 + Google App Engine: Part 6 [Integration Testing using HttpUnit]

In my previous post spring3 + maven2 + Google App Engine: Part 5[Unit Testing] I demonstrated how to create unit test for your Google App Engine application. Unit testing is only one part of the goal of 100% test coverage. The next major topic to cover, and arguably the most difficult to setup is integration tests. Integration test are test that use more than one component to complete the test. In web application testing, this is typically where test that can only be run when there is a running application server.

This post will cover Integration test using HttpUnit and the Failsafe Maven Plugin. Your first question is probably, why use failsafe in addition to the surefire. The quick answer is that the failsafe will continue if your servlets cause an exception allowing you to test all of your files.

Failsafe plugin
We'll get started adding the Failsafe plugin to the POM file. In your pom.xml file, add the following to your section:
<plugin>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>2.6</version>
    <configuration>
        <includes>
            <include>**/*SysTest.java</include>
        </includes>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
            </goals>
        </execution>
    </executions>
</plugin>

When the goal "integration-test" is run, the this testing plugin will include all files named SysTest.java. This should be changed to your specific package setup. Using "SysTest", "ITCase" or other pre/post fixes and separate packages are other options to segregate your integration test from your unit test.

Application Server
We now have the failsafe plugin, but we have to figure out how to run the application server before the integration test run. This is achieved by running the maven-gae-plugin, yes, the same plugin used to run your webapp. In recent releases of the maven-gae-plugin, developers added the stopPort and stopKey methods which allow failsafe to start and stop the application sever. The plugin can used the phases "pre-integration-test" and the "post-integration-test" to start and stop the GAE server.

Here is sample code to add to your pom.xml file inside of the section (typically right below the failsafe-plugin added above:
<plugin>
    <groupId>net.kindleit</groupId>
    <artifactId>maven-gae-plugin</artifactId>
    <version>0.7.1</version>
    <configuration>
        <scanIntervalSeconds>10</scanIntervalSeconds>
        <stopKey>STOP</stopKey>
        <stopPort>8005</stopPort>
    </configuration>
    <executions>
        <execution>
            <id>start-gae</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>start</goal>
            </goals>
            <configuration>
                <scanIntervalSeconds>0</scanIntervalSeconds>
                <daemon>true</daemon>
            </configuration>
        </execution>
        <execution>
            <id>stop-gae</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>stop</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Notice, we are using the "start" verses using "run" because "start" does not include all of the task to "package" the war allowing the failsafe plugin to stop the GAE server.

Adding HttpUnit
In this posting, we will be adding and using HttpUnit as it's one of the most comprehensive and stable HTML unit testing libraries. HttpUnit not only allows you to query your URLs, but you can unit-test JavaScript, see errors for CSS, JavaScript and malformed HTML. HtmlUnit also allows you to emulate various different browsers including FireFox and Internet Explorer.

The integration test are very similar to the unit test buy using the same testing context file, Spring JUnit helper, Google Local Testing helpers and JUnit.

An integration test using HttpUnit
Here is a very basic example of an integration test:
package com.example.web.controllers;

...imports...

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/testApplicationContext.xml"})
public class HomeControllerSysTest extends AbstractJUnit4SpringContextTests {

    private static final Logger log = Logger.getLogger(
            HomeControllerSysTest.class.getName());
    private final LocalServiceTestHelper helper =
            new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());

    @Before
    public void setUp() {
        helper.setUp();
    }

    @After
    public void tearDown() {
        helper.tearDown();
    }

    @Test
    public void testHomeController() throws IOException {
        final String url = "http://localhost:8080/movie/test";

        final WebClient webClient = new WebClient();
        final HtmlPage page = webClient.getPage(url);
        assertEquals("The Page Title", page.getTitleText());

        // there are many different methods to query everything on your
        // page. Please refer to the HttpUnit homepage
        HtmlElement header = page.getElementsByTagName("h1").get(0);
        assertNotNull(header);

        String headerValue = header.getNodeValue();
        assertEquals(headerValue, "Hello World!");
    }
}

How to test
Now, it's time to run the test. There are several methods for running integration test without running the unit test, but those tactics will not be covered in this posting. To run the integration test, type the following:
mvn integration-test

That's it! Between this posting and the previous [Part 5] you should be able to test a large majority of your Java Web Application.

6 comments:

Will said...

Seriously, it is nice to the the test integration stuff coming along in GAE so nicely. Great posts...

Anonymous said...

Seems typo, its failsafe not surefail

Mike! said...

Oops! You're right...updated the code (though, this is is a bit dated since I now like to use Geb or WebDriver for integration testing)

Sven said...

hi,

i have a question to the actual test class there. I see, that you are starting the LocalServiceTestHelper. Now i ask myself, why? You are starting you're dev server via maven and in the test you are not accessing the local test services, but do test via the webclient.

Thank you.

Mike Ensor said...

Hey Sven,

The LocalServiceTestHelper handles the interaction, transaction and rollback of data between your local data store.

Mike!

Sven said...

Ah, i see. OK, thank you.