Introduction

This lightweight library allows you to unit test Wicket pages easily: It launches Jetty to run your webapp in-process and launches the Selenium 2 web driver (can be headless, i.e., no browser needed). Then you can store POJO mock objects into the fields of your Wicket page and use the web driver to drive it.

The startup only takes a few seconds. If you run multiple tests together (as a test suite), the startup will occur only once, so it is pretty fast.

Major enhancements introduced in v3:

  • No need to download and run the Selenium server as it has been completely eliminated.
  • No need to use Spring nor Guice.
  • You can use HTMLUnit in place of a real browser. This may make the tests faster (or at least address some people's conception of Selenium being slow).
  • No need to change your page's Java code to test page navigation.

Why is it useful?

  • The key benefit is that you can control the right thing (user input on the web page and the data your pages get, from the services), and then observe the right thing (HTML DOM elements, possible manipulated by Javascript/AJAX, which is far better than inspecting the internal data structure in your program).
  • Your real application is run. You don't need to modify it in anyway.
  • You can set breakpoints in your test code and your page Java code and step through them in the debugger.
  • It's very easy to implement as it relies on well established tools (Selenium and Jetty). It means it can easily be kept updated with new versions of Wicket.
  • It has special support for HTML pages generated by Wicket so that, e.g., you can wait for the completion of AJAX requests easily or locate an HTML element corresponding to a Wicket component easily.
  • Potentially this approach can be applied to frameworks other than Wicket.

How to use

In your pom.xml, add the dependency and the repository (the example below assumes that you're using TestNG. If you use JUnit, please see here):

<project ...>
        ...
        <dependencies>
                ...
                <dependency>
                        <groupId>com.ttdev</groupId>
                        <artifactId>wpt-core</artifactId>
                        <version>3.0.0-SNAPSHOT</version>
                        <scope>test</scope>
                </dependency>
                <dependency>
                        <groupId>com.ttdev</groupId>
                        <artifactId>wpt-runtime</artifactId>
                        <version>3.0.0-SNAPSHOT</version>
                </dependency>
                <dependency>
                        <groupId>org.testng</groupId>
                        <artifactId>testng</artifactId>
                        <version>5.13.1</version>
                </dependency>
        </dependencies>
        <repositories>
            <repository>
              <id>sonatype</id>
              <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
            </repository>
         </repositories>
</project>

To allow injecting mock objects your Wicket pages, you need to install a global injector:

public class MyApp extends WebApplication {
        ...
        @Override
        protected void init() {
                MockableBeanInjector.installInjector(this, new MockableBeanInjector());
        }
}

In order to launch Jetty, your webapp and Selenium web driver automatically before your page tests, make sure you include the WebPageTestContext in your TestNG suite:

<suite name="wicket-page-test-sample">
        <test verbose="2" name="tests" annotations="JDK">
                <packages>
                        <package name="..."></package>
                </packages>
                <classes>
                        <class name="com.ttdev.wicketpagetest.WebPageTestContext"></class>
                </classes>
        </test>
</suite>

Let's assume that your Wicket page is shown below. It is very simple: it uses a form to get some user input, perform some calculation and then outputs it as a result. The important point is that you should NOT have complex business logic in your Wicket page. Instead, encapsulate the logic into interfaces such as the MyService interface:

<html>
        <form wicket:id="form">
                <input type="text" wicket:id="input">
                <input type="submit" value="OK">
        </form>
        Result: <span wicket:id="result" id="result">abc</span>.
</html>

public class PageContainingForm extends WebPage {
        private MyService service;
        private String input;
        private String result;

        public PageContainingForm(MyService s) {
                this.service = s;
                input = service.getDefaultInput();
                Form<PageContainingForm> form = new Form<PageContainingForm>("form",
                                new CompoundPropertyModel<PageContainingForm>(this)) {

                        @Override
                        protected void onSubmit() {
                                result = service.getResult(input);
                        }
                };
                add(form);
                form.add(new TextField<String>("input"));
                add(new Label("result", new PropertyModel<String>(this, "result")));
        }
}

public interface MyService {
        String getDefaultInput();
        String getResult(String input);
}

Create a TestNG test class. The key is to inject a mock object into the field named "service". The rest of the code will use Selenium to test drive the page:

@Test
public class PageContainingFormTest {
        public void testSubmitForm() {
                // this is the mock object serving as MyService
                MyService mockService = new MyService() {

                        public String getDefaultInput() {
                                return "xyz";
                        }

                        public String getResult(String input) {
                                // simply double the input as the result
                                return input + input;
                        }
                };
                // create a proxy around the mock object but that is serializable
                SerializableProxyFactory factory = new SerializableProxyFactory();
                MyService proxyService = factory.createProxy(MyService.class,
                                mockService);
                // launch Jetty, your webapp and Selenium web driver (by default
                // Firefox)
                WicketSelenium ws = WebPageTestContext.getWicketSelenium();
                // open your page and pass the proxy as the constructor argument
                ws.openNonBookmarkablePage(PageContainingForm.class, proxyService);
                // check if the HTML element with attribute name="input" has a value of
                // "xyz"
                assert ws.getValue(By.name("input")).equals("xyz");
                // click the <input> HTML element whose attribute type="submit"
                ws.click(By.xpath("//input[@type='submit']"));
                // check if the HTML element with attribute id="result" has the body
                // text "xyzxyz"
                assert ws.getText(By.id("result")).equals("xyzxyz");
        }

}

Add PageContainingFormTest to your TestNG test suite:

<suite name="wicket-page-test-sample">
        <test verbose="2" name="tests" annotations="JDK">
                <classes>
                        <class name="com.ttdev.wicketpagetest.sample.plain.PageContainingFormTest"></class>
                        <class name="com.ttdev.wicketpagetest.WebPageTestContext"></class>
                </classes>
        </test>
</suite>

Set up Chrome

  1. Make sure you have Chrome installed (the version directly from Google).
  2. Download the latest version of the Chrome Driver here. It is just a single executable file (e.g., chromedriver.exe). Let's say you have put it into the c:\browser-drivers folder.
  3. In Eclipse, choose Run | Run Configurations, create a new configuration, choose that TestNG XML file and then set the following VM arguments:
      -Dcom.ttdev.wicketpagetest.webdriver.class=org.openqa.selenium.chrome.ChromeDriver
      -Dwebdriver.chrome.driver=c:\browser-drivers\chromedriver.exe
  4. Click Run. The test should pass. If it doesn't work, post to our support forum or the Wicket user mailing list.

Keep informed of updates

To keep informed of updates, please subscribe to the announcement mailing list.