07 October 2009

Introduction to MVP Unit Testing - Part One

In a previous article I gave a basic application based on the Google Web Toolkit (GWT) default starter application to show the configuration of Model View Presenter (MVP), Command, Dependency Injection and Event Bus patterns. One of the main benefits of the MVP pattern is that the decoupling of UI code from business logic makes it easier to unit test, however this was not demonstrated in the previous example. Therefore the aim of this two part article is to introduce some techniques and typical unit testing scenarios for an MVP application using a more substantial example in the form of a Vehicle Details wizard:

Screenshot of the MVP Wizard

The unit testing will cover techniques using both plain JUnit tests with EasyMock for super fast testing of the presenter logic and GWTTestCase unit tests for aspects of the view that really need knowledge of the GWT components during testing.

In part one we will focus on preparing the test environment and getting started with EasyMock. Part two will demonstrate specific MVP testings scenarios with the presenter, view and actions (commands).

Resources

I'll try to keep the code snippets to a minimum. The full source and dependencies are available in the form of an Eclipse 3.5 (+ Google plug-in) project which you can download here.

As well as the Google plug-in, the Wizard MVP example uses the following GWT libraries:

gwt-presenterAn MVP framework;
gwt-dispatchAn implementation of the command pattern;
ginDependency Injection for GWT client-side based on Google's Guice;
gwt-vlA validation library for GWT;
gwt-logA log4j-style logger for GWT.

Note: currently GIN needs to be built from source using SVN/Ant.

You'll also need the following libraries for server side functionality and/or unit testing:

EasyMockA mock object framework;
log4jA logging framework;
Google Guice 2.0A dependency injection framework for Java. A special build of this is supplied with GIN.

Compiling and Running the Example Code

There is an additional module file called WizardMvpTest which is used by the GWTTestCase based tests and is located under the gwt-test source folder. This module will cause havok with the running of the example in hosted mode and final compilation of the project. When creating a run configuration for hosted mode make sure that the GWTTestModule is excluded. Here are the minimum steps required to get a working run configuration:

  1. From the main menu select, Run -> Run Configurations...
  2. Select "Web Application" from the left hand project type list, right click on this option and choose "New";
  3. Under name, give the configuration a sensible name;
  4. On the "Main" tab, browse to the project "wizard_mvp";
  5. On the "GWT" tab, browse to the URL "wizard_mvp.html" and remove WizardMvpTest from the available modules list;
  6. Select Apply to save the settings and, if you wish, select Run to launch the project.

When compiling the GWT project remove the WizardMvpTest module from the available list - unfortunately this action is not remembered so you'll have to remove it on every compile.

A Whistlestop Tour of the Application

The main focus of this article is to talk about unit testing rather than the application itself, so this section will give a very brief overview of the application. At the server we have a simple Guice servlet servicing commands sent from the client. It maintains a very simple data context which has been pre-populated with some data. At the client we begin with a landing page which contains a table showing vehicles currently in the data model and an add button to launch a wizard for entering data.

Most of us will use wizards fairly frequently (maybe you know them as Process Funnels, Process Guides or Stepped Tasks) and in certain situations they can be a good way to guide a user through some complex task. What makes the wizard interesting the point of view of an MVP app is that:

  • The wizard is a composite of many MVP sub-components, appearing as a single component;
  • Wizard sub-components remain decoupled yet operate on a common state;
  • Other components may require notification of events happening around the wizard.

The wizard in this example comprises a three stage process for entering vehicle data. To keep things interesting, the second stage may branch depending on the vehicle type entered at the first step. Also a navigation panel is used to show progress and provide direct access to previously completed steps.

The wizard is broken down into steps (each of which having a view/presenter pair), a navigation panel and a master vehicle wizard presenter/view. The master presenter maintains a reference to each of the steps and provides the usual wizard action buttons (back, next, etc). The master is fairly dumb and mainly acts as a registry for the steps - it is the steps themselves that contain the more interesting logic such as validation and determining which step to go to next.

The whole point of a wizard is to gather user input and to support this a common state object is injected into each step's presenter which is then responsible for binding and validating the data pertinent to that step. Communication between the master wizard presenter and its components is decoupled via the event bus. For example, if we want to navigate to the next step after the next button is clicked, a "next request" event is fired from the master presenter, a step presenter will listen for these requests. Upon delivery, the step presenter will validate the current step and if all is okay then a response is returned instructing the wizard master presenter of where to go next. Decoupling in this way has the following benefits:

  • A step's logic may be asynchronous. For example if a validation step requires a server call then a Boolean return at that point in time is not be possible. Sending a response message allows us to continue when we're ready;
  • Other components such as the navigation progress presenter can listen for these events and update as necessary (as well as send their own navigation requests) without the step or master wizard ever being aware its existence;
  • The process of unit testing is arguably made easier since a component may be tested with minimal knowledge of neighbouring components.

In this example the master wizard presenter is VehicleWizardPresenter which is a fairly simple affair that just registers the required components:


@Inject
public VehicleWizardPresenter(final Display display,
final EventBus eventBus,
final VehicleStateContext wizardContext,
final WidgetDisplayContainer displayContainer,
final VehicleStepNavigationPresenter vehicleStepNavigationPresenter,
final Step1Presenter step1Presenter,
final Step2CarPresenter step2CarPresenter,
final Step2BoatPresenter step2BoatPresenter,
final Step3Presenter step3Presenter) {
super(display, eventBus, wizardContext);

this.displayContainer = displayContainer;

this.vehicleStepNavigationPresenter = vehicleStepNavigationPresenter;

// Register the steps of the wizard.
registerStep(step1Presenter);
registerStep(step2CarPresenter);
registerStep(step2BoatPresenter);
registerStep(step3Presenter);
}

A step within the wizard is identified by an enumerated type VehicleStepEnum:


public enum VehicleStepEnum {
STEP_1,
STEP_2_CAR,
STEP_2_BOAT,
STEP_3;
}

Each step has a view/presenter pair, for example Step1Presenter/Step1View, and in calling registerStep, the master wizard keys the step presenter against the step enum and is able to call upon this look up when it receives instructions relating to a particular step.

As you navigate between the steps using the back/next buttons or the left hand navigation panel, logic in the steps will validate and determine the next step to visit. A good example of this is in step 1 where the vehicle type selection determines which step to go to next (either car or boat). Step 1 is also interesting since it uses server validation to check that the owner name is unique as well as client side validation.

On the last step a summary of the entered data is presented and the user may hit the Finish button to submit the data to the server.

GWTTestCase vs. JUnit

GWT supports unit testing via the GWTTestCase class which acts as a bridge between the JUnit environment and the GWT environment. This is a very powerful tool and there are definitely situations where this is required (which we'll touch upon in part two), however there is a significant overhead to running tests in this way due to GWTTestCase compiling your GWT code prior to running tests. With Test Driven Development (TDD) we need our tests to execute quickly so we can run them often.

An MVP application lends itself well to plain JUnit testing since most of the business logic is decoupled from the view leaving us with POJOs that can be tested without having to use the GWT compiler. The boundary between the presenter and the view is defined using interfaces like HasClickHandlers which are a perfect fit for using mock object libraries like EasyMock to provide a backing implementation during test execution. Using mock objects one can represent these more complicated view objects and verify interaction without having to rely on the presence of GWT components. As a result we can test our presenter logic in a fraction of the time as tests based on GWTTestCase.

Code quality is also improved when using mocks. Mocks are ideal for validating code in a white box manner allowing you verify that methods on third party components are called as expected.

However, there are occasions where GWTTestCase is required, so our testing strategy will be to have frequently executed plain JUnit tests supported by less frequently executed (slower) GWTTestCase tests. To support this strategy we'll define three source folders in Eclipse:

srcContains our GWT application code;
testContains plain JUnit tests and will mimic the GWT client package structure;
gwt-testContains slower GWTTestCase based tests. Will also mimic the GWT client package structure.

A consequence of separating the plain JUnit tests from the GWTTestCase based ones is that we have sacrificed the ability to run all tests together. However the benefit of running the majority of tests quickly makes this worthwhile.

Getting Started with EasyMock

The aim here is get you up and running quickly with EasyMock and demonstrate some test scenarios under a typical MVP application rather than provide an all-encompassing tour of EasyMock. For a more general introduction and more thorough coverage of EasyMock's features I recommend the following articles:

Rather than give tests that execute perfectly first time, the general approach of this article will be to give an initial example that will fail testing due to omissions or errors in EasyMock configuration. Then we'll cover what went wrong and then fix the tests to work. This kind of follows the learning process I went through and will hopefully give a better understanding of what's going on during testing.

Preparing the Test Environment

By default, your GWT application may not have a test folder. If a test folder is required you can add one from the package explorer, select your project and choose New -> Source Folder. Call the new folder test.

Download and add the EasyMock libraries along with the class extensions from here. At the time of writing, EasyMock and the class extensions were at version 2.5.2 and 2.4 respectively. We use the class extensions to create mocks from classes (rather than just interfaces) to support mock testing of presenters which are typically not coded to interfaces.

Extract the EasyMock distros and add asm-attrs.jar, asm.jar, cglib-2.1.3.jar, easymock-2.5.2.jar and easymockclassextension.jar to your project lib folder and build path.

Under your new test source folder, add a package with the same name as an existing package in your client, e.g. co.uk.hivedevelopment.wizardmvp.client.mvp. This convention is important since it gives your test classes access to protected and package scoped resources which will make life easier and testing more thorough due to greater visibility within the test subject classes.

First Test

Let's start by creating a simple test using EasyMock to make sure we have our test configuration setup correctly.

Right click on the new test package and choose New -> JUnit Test Case.

Select "New JUnit 4 test". If this is the first time you're adding a test then Eclipse will give you the option to automatically include the appropriate JUnit dependencies, which you should do.

Call the test anything you like, but stick to a consistent class naming convention such as suffixing PresenterTest. This convention will come into play later when you may have to specify source exclusion rules in view tests (see "Running View Tests" in part two). In this example we've used FirstPresenterTest.

test creation dialog

Now we have a test case we can add a simple EasyMock test:


@Test
public void testFirstEasyMockTest() {
// Create mock object based on HasClickHandlers interface
final HasClickHandlers clickHandlerMock = createMock(HasClickHandlers.class);

// Set expectation - one call to addClickHandler with an argument
// matching the type "ClickHandler" and returning null.
expect(clickHandlerMock.addClickHandler(isA(ClickHandler.class))).andReturn(null);

// Put mock into replay mode
replay(clickHandlerMock);

// TODO: add test code

// Verify all expected were met.
verify(clickHandlerMock);
}

The basic pattern of mock based testing is as follows:

  1. Create the mock object;
  2. Set the expectation of the mock object;
  3. Put mock object into replay mode;
  4. Perform test;
  5. Verify mock object was called as per our expectations.

When a mock is created, it is set in record mode. When in record mode we set expectations by interacting with the mock object calling methods with specific argument values and/or types and a return value (if necessary). Later we place the mock object into "replay" mode and then run our test against the mock, calling "verify" once we're done. Here EasyMock expects the methods, arguments etc. specified in record mode to match those made during the test. If the expected methods and arguments were called then the test passes.

The test will not pass as we set our expectations but did not interact with the mock between replay and verify, so we will get an exception something like the following:

java.lang.AssertionError:
Expectation failure on verify:
addClickHandler(isA(com.google.gwt.event.dom.client.ClickHandler)): expected: 1, actual: 0

To make this test pass, we need to add some code to interact with the mock:


// ...
replay(clickHandlerMock);

// Run test code
clickHandlerMock.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) { }
});

verify(clickHandlerMock);

Now when you re-run the test it should pass.

Presenter Testing with JUnit and EasyMock

Now we have a test environment and EasyMock configured, how do we test a presenter? Using the gwt-presenter library, a simple presenter might look like the following:


public class ExitPagePresenter extends WidgetPresenter<ExitPagePresenter.Display> {

public static Place PLACE = new Place("ExitPage");

public interface Display extends WidgetDisplay {
HasClickHandlers getReturnToStart();

HasHTML getResult();
}

@Inject
public ExitPagePresenter(final Display display, final EventBus eventBus) {
super(display, eventBus);

bind();
}

@Override
public Place getPlace() {
return PLACE;
}

@Override
protected void onBind() {
display.getReturnToStart().addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
// ...
}
});
}

@Override
public void refreshDisplay() {
display.getResult().setHTML("<h1>Hello Test</h1>");
}

// Empty onPlaceRequest(), onUnbind() and revealDisplay() methods omitted
// for brevity.
}

Some points to note here are that:

  1. GIN was used to inject an instance of the Display and EventBus into the class;
  2. We're calling bind() in the constructor which will cause the framework to invoke onBind().

To start testing an instance of this class, we need to supply instances of Display and EventBus to the presenter's contructor. In the client, instances are created and injected using GIN, however this is not available in a JUnit test. That leaves us with two options: manually create the dependent mock objects and supply them to the presenter's constructor; or we can use to Guice, GIN's Java brother, to provide injection instead. I'm going to discount the manual method since there could be rather a lot of presenters in an application and this will amount to too much wiring. That leaves the Guice option. This makes sense since it uses the same @Inject annotation as GIN allowing us to define injectable components which can be used across all tests.

GIN and Guice

If you're not familiar with Guice, I'll refer you at this point to the following articles by Dick Wall which do a fine job of intro-guicing (sorry!) the concepts:

You should be already using GIN in your project, so Guice is already configured in our environment by way of including the guice-2.0.jar library that is bundled with GIN on the build path. Unfortunately our client GIN module is not reusable in the tests, so our first step is to define a Guice module in which to place our mock bindings:


public class WizardTestModule extends AbstractTestModule {

// ...

@Override
protected void configure() {
final EventBus eventBus = createStrictMock(EventBus.class);
bind(EventBus.class).toInstance(eventBus);

// ...

bindPresenter(ExitPagePresenter.class, ExitPagePresenter.Display.class);

// ...
}
}

Where AbstractTestModule is a Guice version of gwt-presenter's GIN module class AbstractPresenterModule and provides some utility methods for defining presenter/display bindings in much the same way as the client except that mock instances are used. Now we define the bindings once and let Guice do the rest for our tests.

See the accompanying Wizard MVP project for a full definition of AbstractTestModule.

To create an injected instance of the presenter for testing using Guice amend your test's setUp method as follows:


public class FirstPresenterTest {

private EventBus eventBus;
private ExitPagePresenter.Display view;
private ExitPagePresenter presenter;

@Before
public void setUp() throws Exception {
final Injector injector = Guice.createInjector(new WizardTestModule(ExitPagePresenter.class));

// get references to mock objects to verify interaction during test
eventBus = injector.getInstance(EventBus.class);

view = injector.getInstance(ExitPagePresenter.Display.class);

// presenter construction will call bind() which may call view and
// eventBus (so put into replay now).
replay(view, eventBus);

presenter = injector.getInstance(ExitPagePresenter.class);

// Verify calls to mock objects were as expected.
verify(eventBus, view);

// Reset so tests can defined their own expectations.
resetToStrict(eventBus, view);
}

// ...
}

Notice how the constructor for the WizardTestModule refers to the test subject ExitPagePresenter.class? This is important because in our Guice test module we define bindings for all our MVP components as mocks, including ExitPagePresenter.class. Since this class is the test subject injector.getInstance(ExitPagePresenter.class) needs to return an injected real instance otherwise we'll be calling mock code and not the class code. To account for this scenario the AbstractTestModule can take a Class argument indicating that the class type should be ignored during the initialisation which allows us to create the correct presenter test instance.

If you run the test case at this point then the tests will fail because we need to set some expectations. Remember from the ExitPagePresenter code above that the constructor calls bind()? This is turn calls onBind() where our presenter registers a click handler with view.getReturnToStart(). The view object is a mock object and if you recall from the first test we need to specify what is returned from the mock. Also during construction, the gwt-presenter framework also registers a PlaceRequestEvent handler if a presenter returns a non-null object from getPlace(). So we have to define the following expectations to make this test pass:


view = injector.getInstance(ExitPagePresenter.Display.class);

// Set expectation - one call to addClickHandler with an argument matching
// the type "ClickHandler" and returning null.
expect(clickHandlerMock.addClickHandler(isA(ClickHandler.class))).andReturn(null);

// Create mock object based on HasClickHandlers interface
final HasClickHandlers returnToStart = createMock(HasClickHandlers.class);

// Specify that we're expecting one call to view.getReturnToStart() which
// will return our mock click handler.
expect(view.getReturnToStart()).andReturn(returnToStart);

// Required by gwt-presenter
expect(eventBus.addHandler(eq(PlaceRequestEvent.getType()), EasyMock.<PlaceRequestHandler> anyObject())).andReturn(null);

replay(view, eventBus, returnToStart);

presenter = injector.getInstance(ExitPagePresenter.class);

verify(view, eventBus, returnToStart);

Note that we've added returnToStart to the replay/verify list.

This code is still not ideal - it's too verbose and we'll be repeating this functionality in many tests. We can define a utility class to reduce these declarations. The wizard project defines an MvpTestHelper class for this purpose and using some of its methods our setUp becomes:


view = injector.getInstance(ExitPagePresenter.Display.class);

final HasClickHandlers returnToStart =
MvpTestHelper.expectHasClickHandlers(view.getReturnToStart(), ClickHandler.class).getValue();

MvpTestHelper.expectAddAnyHandler(eventBus, PlaceRequestEvent.getType());

replay(view, eventBus, returnToStart);

That reduces the code count somewhat. See "Specifying EasyMock Helper Methods" below for details on creating your own helper methods. The above code verifies that our presenter is created and has made the expected bindings once and only once. This is a crucial check since bugs relating to unbound or multiply bound handlers can be a real menace to track down in an MVP application. I had one such case where a wizard step was registering event handlers twice due a class constructor calling bind() as well as in a super class. At runtime, all sorts of weird exceptions were being thrown and at first it wasn't clear why. As soon as mock testing was introduced the bug was revealed very quickly.

Running the test case now should complete setUp cleanly but we've yet to test the presenter. The following is a simple test to verify that calling refreshDisplay() sets the view HTML:


@Test
public void testRefreshDisplay() {
final String expectedHtml = "<h1>Hello Test</h1>";

final HasHTML html = createMock(HasHTML.class);

expect(view.getResult()).andReturn(html);

html.setHTML(expectedHtml);

replay(view, html);

presenter.refreshDisplay();

verify(view, html);
}

The test above will pass. Try commenting out the call to refreshDisplay() or changing the expected HTML value to see how EasyMock reports the omissions.

Nice and Strict

EasyMock defines different modes of operation. Nice mode creates mocks that aren't too fussy about what's called, how many times (if indeed at all) and in what order. As you might expect, strict mode is the opposite where the expectations must be met or else.

By default the AbstractTestModule will create all mocks in nice mode except for the eventBus and dispatchAsync which are created in strict mode since the order and number of events/server calls are something that is highly important to an MVP application. To crank the mock mode up or down you can use the methods EasyMock.resetToNice() and EasyMock.resetToStrict() as appropriate for your tests.

Specifying EasyMock Helper Methods

As your tests build up you will want to add helper methods of your own. When I first created a helper method I ran into a subtle execution trait with EasyMock which caused my helper to mysteriously fail, so to save you the pain it's probably worth pointing it out here. Let's create a simple helper method to demonstrate the issue. The following code might appear frequently so we want to wrap this up in a static helper method:


// Verify that a click handler was registered
final HasClickHandlers returnToStart = createMock(HasClickHandlers.class);

expect(clickHandlerMock.addClickHandler(isA(ClickHandler.class))).andReturn(null);

expect(view.getReturnToStart()).andReturn(returnToStart);

When creating the helper method, my first (naive) attempt was along the follows lines:


public static HasClickHandlers expectHasClickHandlers(final HasClickHandlers hasClickHandlers) {
final HasClickHandlers mockHasClickHandlers = createMock(HasClickHandlers.class);
expect(hasClickHandlers).andReturn(mockHasClickHandlers);

return mockHasClickHandlers;
}

MvpTestHelper.expectHasClickHandlers(view.getReturnToStart(), ClickHandler.class);

If you run this then you'll get an IllegalStateException with the message "no last call on a mock available". The reason is that when we interact with an EasyMock object, the mock state is maintained for the previous call only and a call to createMock will scrub that state. So in wrapping my code in a static method and the order of the calls to view.getReturnToStart(), expect and createMock had changed resulting in the removal of my mock object state.

After reading the above code you be forgiven for think that the call to view.getReturnToStart() is for its value. However, the return value will be null - the call is actually required because it will arm EasyMock with the mock control context for the specific method call and expect will operate on that context. But this context will not survive long since the first line of the naive helper calls createMock which immediately deletes it and this is the cause of our "no last call on a mock available" message when expect is subsequently called. The solve this, we must correct the call order:


public static HasClickHandlers expectHasClickHandlers(final HasClickHandlers hasClickHandlers) {
final IExpectationSetters<HasClickHandlers> expectationSetter = expect(hasClickHandlers);

final HasClickHandlers mockHasClickHandlers = createMock(HasClickHandlers.class);

expectationSetter.andReturn(mockHasClickHandlers);

return mockHasClickHandlers;
}

Now the call order has been fixed, the helper method runs correctly.

Event Handlers and Anonymous Inner Classes

It's quite common to see examples of GWT code that makes use of anonymous inner classes for defining objects such as click handlers:


display.getAdd().addClickHandler(new ClickHandler() {
public void onClick(final ClickEvent event) {
// logic here
}
});

Code in this manner can be quick to write but it's difficult to test. An initial resolution might be to decouple the business logic from inside onClick into a method which is mutually accessible from the handler and the unit test:


display.getAdd().addClickHandler(new ClickHandler() {
public void onClick(final ClickEvent event) {
doLogic();
}
});

void doLogic() {
// logic here
}

While this solves the issue at hand, we're still left with some downsides in that the call to the doLogic method is not covered by a test and may be subject to typos or may require interaction with click event. Also, if you use a code coverage tool then the code within onClick will still not be reachable from tests which might taint your coverage stats. Finally, if your handler has more than one method to implement then your code becomes increasingly messy. I've found that a tidy solution to all the previous issues is to use an inner class instead:


display.getAdd().addClickHandler(new AddClickHandler());

class AddClickHandler implements ClickHandler {
@Override
public void onClick(final ClickEvent event) {
// logic here
}
}

Note that the class is not static (so we have access to everything we had before) and it uses default scoping to make it accessible from the tests. The line count remains very similar to the original anonymous inner class example only this time we can test the onClick method directly (note the slightly unusual syntax for creating an instance of the inner class):


@Test
public void testAdd() {
// ...

final ClickEvent clickEvent = createMock(ClickEvent.class);

final StartPagePresenter.StartInDialogClickHandler handler = presenter.new StartInDialogClickHandler();

replay(...);

handler.onClick(clickEvent);

verify(...);
}

Summary

In this article you have been introduced to some of the techniques that can applied when unit testing your MVP applications using EasyMock and without the need for using GWTTestCase, resulting in tests run much faster. EasyMock provides a powerful mock solution but using the library for the first time can have some unexpected outcomes, some of which we have covered. I hope this article gets you started with testing your MVP applications. In part two I hope to look at some more specific testing scenaros for an MVP application covering more of the presenter, dispatch actions (commands) and testing the view itself with GWTTestCase.