A canonical web test

In order to smoke test web applications, I like to run-to-end smoke tests that start the web server and drives a web browser to interact with the application. Here is how this may look:

<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> BookingWebTest <span style="color: #009900;">{</span>
 
    <span style="color: #000000; font-weight: bold;">private</span> DataSource dataSource<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">private</span> Server server<span style="color: #339933;">;</span>
 
    @Before
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> createServer<span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">{</span>
        dataSource <span style="color: #339933;">=</span> DataSources.<span style="color: #006633;">getTestDataSource</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">new</span> EnvEntry<span style="color: #009900;">(</span><span style="color: #0000ff;">"jdbc/applicationDs"</span>, dataSource<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
 
        server <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Server<span style="color: #009900;">(</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
        server.<span style="color: #006633;">setHandler</span><span style="color: #009900;">(</span><span style="color: #000000; font-weight: bold;">new</span> WebAppContext<span style="color: #009900;">(</span><span style="color: #0000ff;">"src/main/webapp"</span>, <span style="color: #0000ff;">"/test"</span><span style="color: #009900;">)</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
        server.<span style="color: #006633;">start</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">}</span>
 
    <span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> WebDriver browser <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> HtmlUnitDriver<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
 
    @Test
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> shouldShowCreatedBookings<span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #000000; font-weight: bold;">throws</span> <span style="color: #003399;">Exception</span> <span style="color: #009900;">{</span>
        PersonDao personDao <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> PersonDao<span style="color: #009900;">(</span>dataSource<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
 
        Person person <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Person<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
        person.<span style="color: #006633;">setName</span><span style="color: #009900;">(</span><span style="color: #0000ff;">"Person "</span> <span style="color: #339933;">+</span> <span style="color: #003399;">Math</span>.<span style="color: #006633;">random</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
        personDao.<span style="color: #006633;">save</span><span style="color: #009900;">(</span>person<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
 
        browser.<span style="color: #006633;">get</span><span style="color: #009900;">(</span>server.<span style="color: #006633;">getURI</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="color: #339933;">+</span> <span style="color: #0000ff;">"/test/people/"</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
        assertThat<span style="color: #009900;">(</span>browser.<span style="color: #006633;">findElement</span><span style="color: #009900;">(</span>By.<span style="color: #006633;">id</span><span style="color: #009900;">(</span><span style="color: #0000ff;">"people"</span><span style="color: #009900;">)</span><span style="color: #009900;">)</span>.<span style="color: #006633;">getText</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #009900;">)</span>
            .<span style="color: #006633;">contains</span><span style="color: #009900;">(</span>person.<span style="color: #006633;">getName</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">}</span>
<span style="color: #009900;">}</span>

This test is in the actual war module of the project. This is what it does:

  1. Configures the application to run towards a test database
  2. Starts up the web server Jetty on an arbitrary port (port = 0) and deploys the current web application into Jetty
  3. Fires up HtmlUnit which is a simulated web browser
  4. Inserts an object into the database
  5. Navigates to the appropriate location in the application
  6. Verifies that inserted object is present on the appropriate page

This test requires org.eclipse.jetty:jetty-server, org.eclipse.jetty:jetty-webapp and org.seleniumhq.selenium:selenium-htmlunit-driver to be present in the classpath. When I use this technique, I often employ com.h2database:h2 as my database. H2 can run in-memory and so the database is fresh and empty for each test run. The test does not require you to install an application server, use some inane (sorry) Maven plugin or create any weird XML configuration. It doesn’t require that your application runs on Jetty in production or test environment – it work equally fine for Web applications that are deployed to Tomcat, JBoss or any other application server.

Please!

If you are developing a web application for any application server and you are using Maven, this trick has the potential to increase your productivity insanely. Stop what you’re doing and try it out:

  1. Add Jetty and HtmlUnit to your pom.xml
  2. Create a unit test that starts Jetty and navigates to the front page. Verify that the title is what you expect (assertEqual("My Web Application", browser.getTitle()))
  3. Try and run the test

Feel free to contact me if you run into any trouble.

Related:
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.