Testing webapp startup on Jenkins using Maven, Tomcat and Web Driver
Modern web applications often consist of quite some configuration files that should at least be tested for validity. Think of Spring controller configurations, web application descriptors and the like that can’t be tested easily using Unit Tests. Fortunately it’s quite easy to start a tomcat instance on your CI system (Jenkins or Hudson) using the Tomcat Maven Plugin.
As you probably don’t want to start and stop the server on every test run it’s a good idea to bind it to the integration-test phase, probably even to a separate profile that is only triggered on the continuos integration machine. This is what the plugin configuration might look like:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> <configuration> <fork>true</fork> <port>8081</port> </configuration> <executions> <execution> <id>start-tc</id> <phase>pre-integration-test</phase> <goals> <goal>run-war-only</goal> </goals> </execution> <execution> <id>stop-tc</id> <phase>post-integration-test</phase> <goals> <goal>shutdown</goal> </goals> </execution> </executions> </plugin>
We bind the startup to the pre-integration-test phase, which is triggered just before running the integration tests. In post-integration-test we shutdown the server. When running on a CI webapp it’s important to choose a different port than the one that is used for the CI Server as startup will fail when the port is already in use. We are forking to continue with the Maven execution, if you skip this parameter Maven will just stop after it started Tomcat.
If you run the plugin with a failing web configuration (e.g. when you Spring web context breaks) the failures will be logged to the console output. Unfortunately this doesn’t break the build and you won’t notice that there might be a problem.
One way to have the build fail is to add a Test Case that issues a HTTP call to you web application. A good tool for doing this is the Web Driver project which merged with Selenium in Selenium 2.0. Add the dependency to your build:
<dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-htmlunit-driver</artifactId> <version>2.3.1</version> <scope>test</scope> </dependency>
A simple webtest that just calls a page and checks for a certain html element might look like this:
import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.htmlunit.HtmlUnitDriver; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; /** * Simple web test that just queries the login page through the controller. * @author Florian Hopf, Synyx GmbH & Co. KG, hopf@synyx.de */ public class LoginPageWebtest { @Test public void testPage() { WebDriver driver = new HtmlUnitDriver(); driver.get("http://localhost:8081/url/that/redirects/to/login/"); try { // Find the text input element by its name WebElement element = driver.findElement(By.name("username")); assertNotNull(element); } catch (NoSuchElementException ex) { fail("Startup of context failed. See console output for more information, : " + ex.getMessage()); } //Close the browser driver.quit(); } }
Execute the Webtest classes in your profile (we are using a naming convention to distinguish web tests from normal unit tests):
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <executions> <execution> <id>run-webtests</id> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <includes> <include>**/*Webtest.java</include> </includes> <skip>false</skip> </configuration> </execution> </executions> </plugin>
That’s all. In case there is an error in your configuration you will be notified by your CI server that the webtest failed.
Crowie
Good stuff... the selenium reminder is good. Now I have to find the same with Ant and all'll be sweet
Daniel
Good post thanks dude!