Testing views using Gwt-Platform’s MockingBinder tool

Testing GWT views has always been difficult and really slow. This is due to the fact that instantiating javascript components makes it necessary to rely on GwtTestCase, which in turn instantiates HTMLUnit and turns a simple unit test into something that more closely resembles an integration test. In fact, the pain of relying on GwtTestCase is well documented. With the help of GWTMockUtilities, Jukito and our MockingBinder utility, views can be as easily tested as presenters!

Don’t shoot!

A well known trick that anybody who tried testing GWT code learns is to disarm. This is done simply by calling GWTMockUtilities.disarm(). This makes sure any call to GWT.create() returns null instead of launching its javascript infested voodoo.

As your spouse will tell you, however, returning null is not a solution! In many cases, you will run into null pointer exceptions when your code (or GWT’s internal code) expects an object but finds null. The key is therefore to never call GWT.create, either directly or via a call to new that would eventually lead to the dreaded GWT.create().

The first trick: don’t create your UiBinder yourself!

Let’s examine a typical view that relies on UiBinder:

public class HelloViewImpl extends ViewImpl implements HelloView {
  interface Binder extends UiBinder<Widget, HelloViewImpl> {
  }

  private static Binder uiBinder = GWT.create(Binder.class);
  ...
}

Our first call to GWT.create() is right there. Worse, it’s in a static variable, so it will get created as soon as the class is loaded! How do we get rid of this one?

Enters Google GIN, dependency injection at its finest. One feature of GIN is that, if it finds an interface that has not been bound, then it creates it with a call to GWT.create(). This is precisely what we want here. Let’s then rewrite the above to use GIN:

public class HelloViewImpl extends ViewImpl implements HelloView {
  public interface Binder extends UiBinder<Widget, HelloViewImpl> {
  }

  @UiField HasText firstNameTextBox;
  @UiField HasText helloLabel;

  @Inject
  public HelloView(final Binder uiBinder) {
    initWidget(uiBinder.createAndBindUi(this));
  }
  ...
}

You are probably wondering if the binder will be GWT.create()d every time the view is instantiated. Yes. This is not a problem if the view is a singleton, but it can become one if you plan of filling a huge table with instances of that view. For those cases, simply go in your Gin configuration file and bind(HelloViewImpl.Binder.class).in(Singleton.class). We ran some GwtTestCase to verify the exact behavior of GWT.create() and using this singleton trick this will result in the same behavior as using a static field, but without the GWT.create().

Another option would be to inject Binder in a static field and rely on GIN’s requestStaticInjection(). But we all know static injection is evil, right?

What to do when testing?

When testing, you replace GIN with Guice (Is there a morale here?). As a result you will no longer have the automatic call to GWT.create(). This is great, but it also means you have to pass something else as a parameter when instantiating your view.

The first idea could be to simply pass an anonymous Binder class. Remember, however, that one of the side effects of the call to createAndBindUi is to fill all your @UiFields. We need our replacement class to do the same.

This is exactly what MockingBinder sets out to solve! This class uses reflection to identify all you @UiFields and let you use the mocking framework you want to fill them with mocked object of the corresponding type. It also ensures the call to createAndBindUi() returns a mocked object of the right type.

Writing a test using Jukito

When testing, you will need a TestBinder class that both extends MockingBinder and implements your view’s Binder. If you’re doing your unit test the old fashioned way, you can simply pass a new TestBinder() when instantiating your view. However, if you’ve discovered the joy of using DI and automocking right in your unit tests, then you will want to use Jukito. Let’s see how to do this:

@RunWith(JukitoRunner.class)
public class HelloViewTest {
 /**
  * This is the Guice test module. Jukito runs the configureTest() method
  * to setup all the bindings used in the following unit tests.
  */
  public static class Module extends JukitoModule {
   /**
    * This is the test binder extending `MockingBinder` and implementing the view's `Binder`.
    */
    static class TestBinder extends MockingBinder implements Binder {
      @Inject
      public MyTestBinder(final MockitoMockFactory mockitoMockFactory) {
        super(Widget.class, mockitoMockFactory);
      }
    }

    @Override
    protected void configureTest() {
      GWTMockUtilities.disarm();
      bind(Binder.class).to(TestBinder.class);
    }
  }

  @AfterClass
  public static void tearDown() {
    GWTMockUtilities.restore();  // Just to be nice to future tests
  }

  @Inject HelloView view;

  @Test
  public void anyTest() {
    // given
    given(view.firstNameTextBox.getText()).willReturn("Christian");

    // when
    view.sayHello();

    // then
    verify(view.helloLabel).setText("Hello Christian!");
  }
}

And the factory:

public class MockitoMockFactory implements MockFactory {
  @Override
  public  T mock(Class classToMock) {
    return Mockito.mock(classToMock);
  }
}

The first thing we do here, as soon as Jukito’s test module is initializing with the configureTest() method, is to call GWTMockUtilities.disarm(). This ensures nothing bad happens when loading the GWT classes since some static variables are initialized with calls to GWT.create(). For the benefit of future tests, we rearm GWT later in the tearDown() method.

Then we tell Guice that it should use our new TestBinder whenever it needs a Binder. Finally, we inject BlogView in the test class, this is caught by Jukito who uses Guice to instantiate BlogView, filling-in any dependencies. In this case, the TestBinder.

Finally our test can stub and verify behavior on all the @UiField of our view.

What about views that create widgets dynamically?

Some views need to create widgets dynamically, for example to fill a table with rows as data becomes available. Creating these widgets is typically done with new, but this will trigger an internal call to GWT.create() which, as we’ve seen, may later lead to null pointer exceptions.

The key here is to skip the new, again using the magic of dependency injection. Say, for example, you need to create labels dynamically. Simply inject a Provider<Label> in your view and call labelProvider.get() whenever you need a new instance. In your production code, this will have exactly the same effect as calling new Label(). In your testing code, however, you can substitute the Provider<Label> for a class that generates mock labels. In fact, this is exactly what Jukito will do for you provided you include this line in your configureTest() method: forceMock(UiObject.class);

If you want to dynamically instantiate widgets that require construction parameters you may need to create a factory, or to rely on Guice’s assisted factories which will be coming soon to GIN.

Philosophical questions…

One of the goals of the model-view-presenter architecture was to remove all the logic from the views so that you can test the core of your application without having to rely on GwtTestCase. With UiBinder, you end up with something that looks like MVVP since the declarative UI can be somehow seen as a dumb view. With the approach proposed here, you can pack as much logic as you want in your view and test it as easily as if it were a presenter. Does it mean we should drop one V on the MVVP pattern? What do you think?

More examples

For more complete examples, the ArcBees hive project heavily relies on this pattern to test views that have a bit of logic in them.