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.

Suppose a simple UI comprising a bunch of server-side generated HTML pages, such as our app. Never mind the shortcomings in user experience, the simple design embraces a smooth test automation. Let's get started..

Using your favourite IDE, open the project we created in the previous post or download the source code.

We start off by modifying the pom.xml. Feel free to download the complete build configuration from the repository, should you get lost in any of the steps below.

As the first step, we import JUnit and HtmlUnit libraries and make them available for testing, in pom.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>  
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:
 <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>  
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:
 $ mvn -Pintegration verify  
 ...  
 [INFO] ------------------------------------------------------------------------  
 [INFO] BUILD SUCCESS  
 [INFO] ------------------------------------------------------------------------  
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:
import com.gargoylesoftware.htmlunit.WebClient;
public class SimpleWebIT {  
   private String indexUrl;  
   private WebClient webClient;  
 }  
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:
 public class SimpleWebIT {  
   ...  
   @Before  
   public void setUp() {  
     indexUrl = System.getProperty("integration.base.url");  
     webClient = new WebClient();  
   }  
   @After  
   public void tearDown() {  
     webClient.closeAllWindows();  
   }  
   ...  
 }  
Note that instead of hard coding the app url we access the value as system property wisely exported in the build script:
 <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>  
          ...  
Our first test verifies that the front page provides means of how to submit your name:
   @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);  
   }  
   
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:
   @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 + "!"));  
   }  
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.