Skip to main content

Lift Off

Posted by tomwhite on October 30, 2006 at 3:50 AM PST

In a previous blog entry I mentioned a literate functional testing framework that we had developed at our company, Kizoom. The framework was initially developed by my colleague Robert Chatley for testing a rather obscure digital TV XML application. After that project we produced a new version of the framework for testing HTML applications, which we used internally for a number of projects with great success. Last month we spend a bit of time cleaning up the API and released LiFT, short for Literate Functional Testing, as an open source project on java.net.

Here I'd like to give a flavour of LiFT and why you might want to use it.

Kizoom Summer Party 2004
When you set out to test your web application you need to think about the mechanics of how you interact with the application. Typically this involves fetching pages, finding elements on the page that you want to read (so you can make an assertion about them) and elements you want to change (so you can simulate user input). Often, the code required to handle these aspects obscures the intent of the test. Consider this HttpUnit test to do a Google search for an image of our ill-fated company summer party of two years ago:

import junit.framework.TestCase;

import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebForm;
import com.meterware.httpunit.WebImage;
import com.meterware.httpunit.WebResponse;

public class HttpUnitGoogleTest extends TestCase {

    public void testGoogleImageSearch() throws Exception {
        WebConversation conversation = new WebConversation();
        WebResponse page = conversation.getResponse("http://www.google.com/");
        assertEquals(page.getTitle(), "Google");
        page = page.getLinkWith("Images").click();
        WebForm form = page.getForms()[0];
        form.setParameter("q", "kizoom");
        page = form.submit();
        assertTrue(page.getText().contains("<b>Kizoom</b> summer party"));
        boolean foundImage = false;
        for (WebImage i : page.getImages()) {
            if (i.getSource().endsWith("summer04.jpg")) {
                foundImage = true;
                break;
            }
        }
        assertTrue(foundImage);
    }

}

Hmm, not too clear what it's testing. I'm not trying to single out HttpUnit here - it's a great tool for interacting with web applications, but it's pretty low-level. Of course, I could abstract some of the operations into helper methods to make the test clearer. For instance, the bit at the end where I'm looking for an image is ripe for extracting as a method. In fact, this is the approach we took for years - building libraries of helper functions that made using HttpUnit a bit less of an effort. However, it's not a structured approach. We can do better.

import com.kizoom.lift.NavigatingLiftTestCase;

import static com.kizoom.lift.constraints.Constraints.*;
import static com.kizoom.lift.matcher.Matchers.*;

public class GoogleTest extends NavigatingLiftTestCase {

    public void testGoogleImageSearch() throws Exception {
        goTo("http://www.google.com/");
        assertThat(page, has(title("Google")));
        clickOn(link("Images"));
        enter("kizoom", into(textField()));
        clickOn(button("Search Images"));
        assertThat(page, has(text("Kizoom summer party")));
        assertThat(it, has(image().withUrlThat(endsWith("summer04.jpg"))));
    }
}

This is much more concise. But more importantly it is much more readable. Each line can be read as a sentence, either as a jMock-style flexible assertion starting with "assert that", or as a command, such as "click on".

Page navigation is handled for us - we are provided with a page variable which always refers to the current page we are on.

Perhaps the most powerful idea is in the use of matchers. Matchers allow us to refer to bits of the page - so we can assert something about them or do something to them. For example, title("Google") creates a matcher that matches HTML title elements with text content "Google". We then use the has constraint to assert that there is (at least) one such match.
Or take the next line: link("Images") matches a link with text "Images" which we use to navigate to the next page by means of the clickOn action.

Matchers allow for extensibility - it is easy to write your own if you need to - and help avoid the creation of ad hoc helper method libraries. Often you don't even need to go this far, you can refine an existing matcher using any jMock constraint. This is what is done in the last line. The withUrlThat refinement on the image matcher limits the matches to those that meet the given constraint. In this case we use the jMock endsWith constraint to match the image filename.

LiFT still has some rough edges - for example, its error messages are not always as clear as they could be - but it is definitely ready for testing real web applications.
There's much more I could say, but I won't here. If you want to learn more try LiFT out by following the introduction, or read the slides from the talk Robert and I gave at the Google London Test Automation Conference in September, or even watch the video.

Related Topics >>