23. Test Driven Development with Wicket

Test Driven Development has become a crucial activity for every modern development methodology. This chapter will cover the built-in support for testing provided by Wicket with its rich set of helper and mock classes that allows us to test our components and our applications in isolation (i.e without the need for a servlet container) using JUnit, the de facto standard for Java unit testing.

In this chapter we will see how to write unit tests for our applications and components and we will learn how to use helper classes to simulate user navigation and write acceptance tests without the need of any testing framework other than JUnit.

The JUnit version used in this chapter is 4.x.

23.1. Utility class WicketTester

A good way to start getting confident with Wicket unit testing support is looking at the test case class TestHomePage that is automatically generated by Maven when we use Wicket archetype to create a new project:

mvn wicket archetype

Here is the content of TestHomePage:

  1. public class TestHomePage{
  2. private WicketTester tester;
  3. @Before
  4. public void setUp(){
  5. tester = new WicketTester(new WicketApplication());
  6. }
  7. @Test
  8. public void homepageRendersSuccessfully(){
  9. //start and render the test page
  10. tester.startPage(HomePage.class);
  11. //assert rendered page class
  12. tester.assertRenderedPage(HomePage.class);
  13. }
  14. }

The central class in a Wicket testing is org.apache.wicket.util.tester.WicketTester. This utility class provides a set of methods to render a component, click links, check if page contains a given component or a feedback message, and so on.

The basic test case shipped with TestHomePage illustrates how WicketTester is typically instantiated (inside method setUp()). In order to test our components, WicketTester needs to use an instance of WebApplication. Usually, we will use our application class as WebApplication, but we can also decide to build WicketTester invoking its no-argument constructor and letting it automatically build a mock web application (an instance of class org.apache.wicket.mock.MockApplication).

The code from TestHomePage introduces two basic methods to test our pages. The first is method startPage that renders a new instance of the given page class and sets it as current rendered page for WicketTester. The second method is assertRenderedPage which checks if the current rendered page is an instance of the given class. In this way if TestHomePage succeeds we are sure that page HomePage has been rendered without any problem. The last rendered page can be retrieved with method getLastRenderedPage.

That’s only a taste of what WicketTester can do. In the next paragraphs we will see how it can be used to test every element that composes a Wicket page (links, models, behaviors, etc…​).

23.1.1. Testing links

A click on a Wicket link can be simulated with method clickLink which takes in input the link component or the page-relative path to it.

To see an example of usage of clickLink, let’s consider again project LifeCycleStagesRevisited. As we know from chapter 5 the home page of the project alternately displays two different labels (“First label” and “Second label”), swapping between them each time button “reload” is clicked. The code from its test case checks that label has actually changed after button “reload” has been pressed:

  1. //...
  2. @Test
  3. public void switchLabelTest(){
  4. //start and render the test page
  5. tester.startPage(HomePage.class);
  6. //assert rendered page class
  7. tester.assertRenderedPage(HomePage.class);
  8. //assert rendered label
  9. tester.assertLabel("label", "First label");
  10. //simulate a click on "reload" button
  11. tester.clickLink("reload");
  12. //assert rendered label
  13. tester.assertLabel("label", "Second label");
  14. }
  15. //...

In the code above we have used clickLink to click on the “reload” button and force page to be rendered again. In addition, we have used also method assertLabel that checks if a given label contains the expected text.

By default clickLink assumes that AJAX is enabled on client side. To switch AJAX off we can use another version of this method that takes in input the path to the link component and a boolean flag that indicates if AJAX must be enabled (true) or not (false).

  1. //...
  2. //simulate a click on a button without AJAX support
  3. tester.clickLink("reload", false);
  4. //...

23.1.2. Testing component status

WicketTester provides also a set of methods to test the states of a component. They are:

  • assertEnabled(String path)/assertDisabled(String path): they test if a component is enabled or not.

  • assertVisible(String path)/assertInvisible(String path): they test component visibility.

  • assertRequired(String path): checks if a form component is required.

In the test case from project CustomDatepickerAjax we used assertEnabled/assertDisabled to check if button “update” really disables our datepicker:

  1. //...
  2. @Test
  3. public void testDisableDatePickerWithButton(){
  4. //start and render the test page
  5. tester.startPage(HomePage.class);
  6. //assert that datepicker is enabled
  7. tester.assertEnabled("form:datepicker");
  8. //click on update button to disable datepicker
  9. tester.clickLink("update");
  10. //assert that datepicker is disabled
  11. tester.assertDisabled("form:datepicker");
  12. }
  13. //...

23.1.3. Testing components in isolation

Method startComponentInPage(Component) can be used to test a component in isolation. The target component is rendered in an automatically generated page and both onInitialize() and onBeforeRender() are executed. In the test case from project CustomFormComponentPanel we used this method to check if our custom form component correctly renders its internal label:

  1. //...
  2. @Test
  3. public void testCustomPanelContainsLabel(){
  4. TemperatureDegreeField field = new TemperatureDegreeField("field", Model.of(0.00));
  5. //Use standard JUnit class Assert
  6. Assert.assertNull(field.get("mesuramentUnit"));
  7. tester.startComponentInPage(field);
  8. Assert.assertNotNull(field.get("mesuramentUnit"));
  9. }
  10. //...

23.1.4. Testing the response

WicketTester allows us to access to the last response generated during testing with method getLastResponse(). The returned value is an instance of class MockHttpServletResponse that provides helper methods to extract information from mocked request.

In the test case from project CustomResourceMounting we extract the text contained in the last response with method getDocument and we check if it is equal to the RSS feed used for the test:

  1. //...
  2. @Test
  3. public void testMountedResourceResponse() throws IOException, FeedException{tester.startResource(new RSSProducerResource());
  4. String responseTxt = tester.getLastResponse().getDocument();
  5. //write the RSS feed used in the test into a ByteArrayOutputStream
  6. ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  7. Writer writer = new OutputStreamWriter(outputStream);
  8. SyndFeedOutput output = new SyndFeedOutput();
  9. output.output(RSSProducerResource.getFeed(), writer);
  10. //the response and the RSS must be equal
  11. Assert.assertEquals(responseTxt, outputStream.toString());
  12. }
  13. //...

To simulate a request to the custom resource we used method startResource which can be used also with resource references.

getLastResponse() should be used to assert the status code, response headers, binary content and anything that is part of the HTTP response.

23.1.5. Testing URLs

WicketTester can be pointed to an arbitrary URL with method executeUrl(String url). This can be useful to test mounted pages, resources or request mappers:

  1. //...
  2. //the resource was mapped at '/foo/bar'
  3. tester.executeUrl("./foo/bar");
  4. //...

23.1.6. Testing AJAX components

If our application uses AJAX to refresh components markup, we can test if AjaxRequestTarget contains a given component with WicketTester‘s method assertComponentOnAjaxResponse:

  1. //...
  2. //test if AjaxRequestTarget contains a component (using its instance)
  3. tester.assertComponentOnAjaxResponse(amountLabel);
  4. //...
  5. //test if AjaxRequestTarget contains a component (using its path)
  6. tester.assertComponentOnAjaxResponse("pathToLabel:labelId");

It’s also possible to use method isComponentOnAjaxResponse(Component cmp) to know if a component has been added to AjaxRequestTarget:

  1. //...
  2. //test if AjaxRequestTarget does NOT contain amountLabel
  3. assertFalse(tester.isComponentOnAjaxResponse(amountLabel));
  4. //...

23.1.7. Testing AJAX events

Behavior AjaxEventBehavior and its subclasses can be tested simulating AJAX events with WicketTester‘s method executeAjaxEvent(Component cmp, String event). Here is the sample code from project TestAjaxEventsExample:

Home page code:

  1. public class HomePage extends WebPage {
  2. public static String INIT_VALUE = "Initial value";
  3. public static String OTHER_VALUE = "Other value";
  4. public HomePage(final PageParameters parameters) {
  5. super(parameters);
  6. Label label;
  7. add(label = new Label("label", INIT_VALUE));
  8. label.add(new AjaxEventBehavior("click") {
  9. @Override
  10. protected void onEvent(AjaxRequestTarget target) {
  11. //change label's data object
  12. getComponent().setDefaultModelObject(
  13. OTHER_VALUE);
  14. target.add(getComponent());
  15. }
  16. }).setOutputMarkupId(true);
  17. //...
  18. }
  19. }

Test method:

  1. @Test
  2. public void testAjaxBehavior(){
  3. //start and render the test page
  4. tester.startPage(HomePage.class);
  5. //test if label has the initial expected value
  6. tester.assertLabel("label", HomePage.INIT_VALUE);
  7. //simulate an AJAX "click" event
  8. tester.executeAjaxEvent("label", "click");
  9. //test if label has changed as expected
  10. tester.assertLabel("label", HomePage.OTHER_VALUE);
  11. }

23.1.8. Testing AJAX behaviors

To test a generic AJAX behavior we can simulate a request to it using WicketTester‘s method executeBehavior(AbstractAjaxBehavior behavior):

  1. //...
  2. AjaxFormComponentUpdatingBehavior ajaxBehavior =
  3. new AjaxFormComponentUpdatingBehavior("change"){
  4. @Override
  5. protected void onUpdate(AjaxRequestTarget target) {
  6. //...
  7. }
  8. };
  9. component.add(ajaxBehavior);
  10. //...
  11. //execute AJAX behavior, i.e. onUpdate will be invoked
  12. tester.executeBehavior(ajaxBehavior));
  13. //...

23.1.9. Using a custom servlet context

In paragraph 16.13 we have seen how to configure our application to store resource files into a custom folder placed inside webapp root folder (see project CustomFolder4MarkupExample).

In order to write testing code for applications that use this kind of customization, we must tell WicketTester which folder to use as webapp root. This is necessary as under test environment we don’t have any web server, hence it’s impossible for WicketTester to retrieve this parameter from servlet context.

Webapp root folder can be passed to WicketTester‘s constructor as further parameter like we did in the test case of project CustomFolder4MarkupExample:

  1. public class TestHomePage{
  2. private WicketTester tester;
  3. @Before
  4. public void setUp(){
  5. //build the path to webapp root folder
  6. File curDirectory = new File(System.getProperty("user.dir"));
  7. File webContextDir = new File(curDirectory, "src/main/webapp");
  8. tester = new WicketTester(new WicketApplication(), webContextDir.getAbsolutePath());
  9. }
  10. //test methods...
  11. }
After a test method has been executed, we may need to clear any possible side effect occurred to the Application and Session objects. This can be done invoking WicketTester‘s method destroy():
  1. @After
  2. public void tearDown(){
  3. //clear any side effect occurred during test.
  4. tester.destroy();
  5. }

23.2. Testing Wicket forms

Wicket provides utility class FormTester that is expressly designed to test Wicket forms. A new FormTester is returned by WicketTester‘s method newFormTester(String, boolean) which takes in input the page-relative path of the form we want to test and a boolean flag indicating if its form components must be filled with a blank string:

  1. //...
  2. //create a new form tester without filling its form components with a blank string
  3. FormTester formTester = tester.newFormTester("form", false);
  4. //...

FormTester can simulate form submission with method submit which takes in input as optional parameter the submitting component to use instead of the default one:

  1. //...
  2. //create a new form tester without filling its form components with a blank string
  3. FormTester formTester = tester.newFormTester("form", false);
  4. //submit form with default submitter
  5. formTester.submit();
  6. //...
  7. //submit form using inner component 'button' as alternate button
  8. formTester.submit("button");

If we want to submit a form with an external link component we can use method submitLink(String path, boolean pageRelative) specifying the path to the link.

In the next paragraphs we will see how to use WicketTester and FormTester to interact with a form and with its children components.

23.2.1. Setting form components input

The purpose of a HTML form is to collect user input. FormTester comes with the following set of methods that simulate input insertion into form’s fields:

  • setValue(String path, String value): inserts the given textual value into the specified component. It can be used with components TextField and TextArea. A version of this method that accepts a component instance instead of its path is also available.

  • setValue(String checkboxId, boolean value): sets the value of a given CheckBox component.

  • setFile(String formComponentId, File file, String contentType): sets a File object on a FileUploadField component.

  • select(String formComponentId, int index): selects an option among a list of possible options owned by a component. It supports components that are subclasses of AbstractChoice along with RadioGroup and CheckGroup.

  • selectMultiple(String formComponentId, int[] indexes): selects all the options corresponding to the given array of indexes. It can be used with multiple-choice components like CheckGroup or ListMultipleChoice.

setValue is used inside method insertUsernamePassword to set the username and password fields of the form used in project StatelessLoginForm:

  1. protected void insertUsernamePassword(String username, String password) {
  2. //start and render the test page
  3. tester.startPage(HomePage.class);
  4. FormTester formTester = tester.newFormTester("form");
  5. //set credentials
  6. formTester.setValue("username", username);
  7. formTester.setValue("password", password);
  8. //submit form
  9. formTester.submit();
  10. }

23.2.2. Testing feedback messages

To check if a page contains one or more expected feedback messages we can use the following methods provided by WicketTester:

  • assertFeedback(String path, String…​ messages): asserts that a given panel contains the specified messages

  • assertInfoMessages(String…​ expectedInfoMessages): asserts that the expected info messages are rendered in the page.

  • assertErrorMessages(String…​ expectedErrorMessages): asserts that the expected error messages are rendered in the page.

assertInfoMessages and assertErrorMessages are used in the test case from project StatelessLoginForm to check that form generates a feedback message in accordance with the login result:

  1. @Test
  2. public void testMessageForSuccessfulLogin(){
  3. inserUsernamePassword("user", "user");
  4. tester.assertInfoMessages("Username and password are correct!");
  5. }
  6. @Test
  7. public void testMessageForFailedLogin (){
  8. inserUsernamePassword("wrongCredential", "wrongCredential");
  9. tester.assertErrorMessages("Wrong username or password");
  10. }

23.2.3. Testing models

Component model can be tested as well. With method assertModelValue we can test if a specific component has the expected data object inside its model.

This method has been used in the test case of project ModelChainingExample to check if the form and the drop-down menu share the same data object:

  1. @Test
  2. public void testFormSelectSameModelObject(){
  3. PersonListDetails personListDetails = new PersonListDetails();
  4. DropDownChoice dropDownChoice = (DropDownChoice) personListDetails.get("persons");
  5. List choices = dropDownChoice.getChoices();
  6. //select the second option of the drop-down menu
  7. dropDownChoice.setModelObject(choices.get(1));
  8. //start and render the test page
  9. tester.startPage(personListDetails);
  10. //assert that form has the same data object used by drop-down menu
  11. tester.assertModelValue("form", dropDownChoice.getModelObject());
  12. }

23.3. Testing markup with TagTester

If we need to test component markup at a more fine-grained level, we can use class TagTester from package org.apache.wicket.util.tester.

This test class allows to check if the generated markup contains one or more tags having a given attribute with a given value. TagTester can not be directly instantiated but it comes with three factory methods that return one or more TagTester matching the searching criteria. In the following test case (from project TagTesterExample) we retrieve the first tag of the home page (a tag) having attribute class equal to myClass:

HomePage markup:

  1. <html xmlns:wicket="http://wicket.apache.org">
  2. <head>
  3. <meta charset="utf-8" />
  4. <title></title>
  5. </head>
  6. <body>
  7. <span class="myClass"></span>
  8. <div class="myClass"></div>
  9. </body>
  10. </html>

Test method:

  1. @Test
  2. public void homePageMarkupTest()
  3. {
  4. //start and render the test page
  5. tester.startPage(HomePage.class);
  6. //retrieve response's markup
  7. String responseTxt = tester.getLastResponse().getDocument();
  8. TagTester tagTester = TagTester.createTagByAttribute(responseTxt, "class", "myClass");
  9. Assert.assertNotNull(tagTester);
  10. Assert.assertEquals("span", tagTester.getName());
  11. List<TagTester> tagTesterList = TagTester.createTagsByAttribute(responseTxt,
  12. "class", "myClass", false);
  13. Assert.assertEquals(2, tagTesterList.size());
  14. }

The name of the tag found by TagTester can be retrieved with its method getName. Method createTagsByAttribute returns all the tags that have the given value on the class attribute. In the code above we have used this method to test that our markup contains two tags having attribute class equal to myClass.

Another utility class that comes in handy when we want to test components markup is ComponentRenderer in package org.apache.wicket.core.util.string. The purpose of this class is to render a page or a component in isolation with its static methods renderComponent and renderPage. Both methods return the generated markup as CharSequence:

  1. @Test
  2. public void customComponentMarkupTest()
  3. {
  4. //instantiate MyComponent
  5. MyComponent myComponent = //...
  6. //render and save component markup
  7. String componentMarkup = ComponentRenderer.renderComponent(myComponent);
  8. //perform test operations
  9. //...
  10. }

23.4. Summary

With a component-oriented framework we can test our pages and components as we use to do with any other Java entity. Wicket offers a complete support for writing testing code, offering built-in tools to test nearly all the elements that build up our applications (pages, containers, links, behaviors, etc…​).

The main entity discussed in this chapter has been class WicketTester which can be used to write unit tests and acceptance tests for our application, but we have also seen how to test forms with FormTester and how to inspect markup with TagTester.

In addition to learning how to use the utility classes provided by Wicket for testing, we have also experienced the innovative approach of Wicket to web testing that allows to test components in isolation without the need of running our tests with a web server and depending only on JUnit as testing framework.