JEE Series: JSP and Integration Tests via HtmlUnit
In the previous post we experimented with JavaServer Pages and created a simple web app. Lazy coders as we are can’t help, but automate as much as possible in order to avoid tedious manual steps. HtmlUnit is very well suited to work in tandem with JSP technology. I value the framework for its ease of use and succinct API.
To be honest with you I do have some reservations about JavaServer Pages. It’s an ageing technology for first, and it doesn’t quite promote best coding practices either. On the other hand, JSP really shines when it comes to getting the job done and as such it has won wide adoption in production systems.
[xml]
<dependencies>
…
<!– Unit and Integration Tests –>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.15</version>
<scope>test</scope>
</dependency>
</dependencies>
[/xml]
Now it’s on time to configure how the integration tests will be conducted. To keep things nice and easy let’s assume the app is up and running before the test automation kicks in. For the sake of an easy maintenance of our build scripts, let’s create a dedicated profile called integration:
[xml]
<profiles>
<profile>
<id>integration</id>
<build>
<!– Integration Tests –>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.18.1</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<!– Remember to deploy the app and start the server in advance –>
<integration.base.url>http://localhost:8080/SimpleWebJSP</integration.base.url>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
[/xml]
Before we move on, it’s a good idea to verify there are no errors as a result of the changes made to the build script. Start the app (see integration.base.url) and run integration tests using the new profile. You should get a successful build:
[bash]
$ mvn -Pintegration verify
…
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[/bash]
That’s it for the configuration, now we are in a position to write the actual tests. Test classes are expected to be found in src/main/test. If you are new to this, there are two kinds of tests. Unit tests, which run during the test phase (mvn test). Any class ending in Test will be considered a unit-test class. Another type of tests are integration tests. Classes ending in IT will be run during the integration-test phase (mvn integration-test or in our case mvn verify). More on Maven life cycle can be found here.
Now, create a new package in src/main/test and add a new class called SimpleWebIT:
[bash]
import com.gargoylesoftware.htmlunit.WebClient;
public class SimpleWebIT {
private String indexUrl;
private WebClient webClient;
}
[/bash]
Our class remains relatively independent from the underlying test framework. We are not going to inherit from any proprietary parent class. The indexUrl is simply a link to the app’s front page. The webClient is a HtmlUnit feature and simulates a browser.
To ensure each of the tests we are going to write runs under the exact same circumstances we add initialisation and clean-up hooks:
[java]
public class SimpleWebIT {
…
@Before
public void setUp() {
indexUrl = System.getProperty(“integration.base.url”);
webClient = new WebClient();
}
@After
public void tearDown() {
webClient.closeAllWindows();
}
…
}
[/java]
Note that instead of hard coding the app url we access the value as system property wisely exported in the build script:
[xml]
<plugin>
…
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
…
<configuration>
<systemPropertyVariables>
<integration.base.url>
http://localhost:8080/SimpleWebJSP
</integration.base.url>
</systemPropertyVariables>
</configuration>
…
[/xml]
Our first test verifies that the front page provides means of how to submit your name:
[java]
@Test
public void your_name_can_be_submitted() throws IOException {
// Open the home page
HtmlPage page = webClient.getPage(indexUrl);
// Find the relevant form by name
HtmlForm form = page.getFormByName(“SubmitYourName”);
// Fill in your name
HtmlTextInput textField = form.getInputByName(“yourName”);
textField.setValueAttribute(“Test Name”);
// Submit the form and assert the response
HtmlSubmitInput button = form.getInputByValue(“OK”);
WebResponse response = button.click().getWebResponse();
String responseUrl = response.getWebRequest().getUrl().toString();
assertEquals((indexUrl + “/response.jsp?yourName=Test+Name”), responseUrl);
}
[/java]
Hope the code comments are explanatory enough. Note how we leverage the fact that the form is handled via HTTP GET.
Our second and final test looks at the response page which is supposed to display the provided user name:
[java]
@Test
public void the_submitted_name_is_displayed() throws IOException {
final String testName = “MyName”;
HtmlPage page = webClient.getPage(indexUrl + “/response.jsp?yourName=” + testName);
assertTrue(page.asXml().contains(“Hello ” + testName + “!”));
}
[/java]
Once again, we rely on the query string containing the submitted user name.
The complete test class can be downloaded from repository.
Hope you appreciate the beauty of HtmlUnit’s API. Our tests might be simple and straightforward, but they merely scratch the surface. In real-life projects we would have to pay attention to additional details. Such as page load times, user journey (transition from page A to B) etc. Also, asynchrony via AJAX would make the tests a little bit more tricky to write.
Nevertheless, I hope you enjoyed this brief insight into functional test automation. Next time we continue with our JEE endeavours and explore Struts, one of the most popular MVC frameworks. Stay tuned.