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.

19 comments

  1. Denis · November 27, 2010

    Great post.
    Very smart to move DI into the View.

    Some remarks :

    You use BDD style in your test (given/when/then), so I would rather use Mockito’s BDD methods :
    given(view.firstNameTextBox.getText()).willReturn("Christian");
    instead of
    when(view.firstNameTextBox.getText()).thenReturn("Christian");

    Do you really need to disarm / restore in the test ? Shouldn’t it be done in Jukito, as you probably won’t mix Jukito with GWTTestCase ?

    About the philosophical stuff :

    Container Dependence :
    It seems to me that for your tests you’re completely dependant on the container now (Gin/Guice) (I may be wrong as I didn’t get all the Guice voodoo), this is the kind of thing DI aims to avoid.
    You may be dependant on the container to wire the application, but your unit tests should be able to work with plain old java / junit. Otherwise this is a sign of an invasive infrastructure.

    GWT dependence :
    With regular MVP, the logic of your UI is dependant on the view’s interface (which is a regular Java interface).
    But with what your proposing here (logic and testing at the View level) your logic is now dependant on specific implementation of UI components (GWT TextBox, …), instead of interfaces (HasText, …). It makes your UI logic more fragile as changing the widgets would require updating the tests. And your UI code dependant on a “bigger thing” (GWT stuff) instead of interfaces.

    Anyway great post !
    Great to see that there’s people putting efforts in trying to make GWT testing easier, we need that ! 🙂

    Denis

  2. Christian Goudreau · November 27, 2010

    You use BDD style in your test
    Right ! I didn’t even know that given existed ! Thanks.

    – Do you really need to disarm / restore in the test ? Shouldn’t it be done in Jukito, as you probably won’t mix Jukito with GWTTestCase ?
    Yes for the first question and no for the second because we want Jukito to be independant from Gwt. I usually create a super classe to handle disarm / restore when I need to test a view.

    – you’re completely dependant on the container now (Gin/Guice)
    Yes and that’s a choice we made while creating Jukito. It may feel invasive, but it feels right to us. What if dependency injection was part of Java ? An example of a language using DI in the core language. http://code.google.com/p/noop/

    Also, MockingBinder doesn’t depend on DI or any Mocking framework, it can also be used without Gwt-Platform.

    – GWT dependence:
    Well… I was agreeing with you but I was wondering if we could change our @UiField to use and interface instead of HTMLPanel (or any other widget) and WE CAN ! I was able to write:

    @UiField
    HasWidgets.ForIsWidget myHtmlPanel;

    I think I’ll made some update to the post to reflect what we said.

    Cheers and thanks for the comment !

  3. Denis · November 27, 2010

    GWT dependence:
    Yes, I realized just after my post that it should be possible to depend on interfaces in the view instead of Widgets 🙂

    Container dependence :
    Interesting, you made a strong choice, a compromise to be more tied to the container, is it worth it ?
    Do you have some numbers ? For example the LOC with/without Jukito for the same non trivial GWT functionality, is it a lot more readable, … ?
    I would be interested to see a comparison between the two approaches, because I feel (I am probably not the only one 🙂 that there’s really a lot of code to write when using MVP.

    Cheers,

    Denis

    • Christian Goudreau · November 27, 2010

      Is it worth it ? Do you have some numbers ?
      Totally worth it, but unfortunately we didn’t make the comparison though it’s planned on Jukito’s wiki. Just think about this, Jukito automatically mock every unbound interface. There’s a lot of boiler plate here that you don’t need anymore ! Creating a new element is just a matter of Injecting the type and if you don’t need to bind anything, you’re done ! The minimal requirement is to use JukitoRunner !

      is it a lot more readable
      Well, that’s arguable, I really think it is, but some neophyte could have difficulties reading Guice’s module configuration.

      I would be interested to see a comparison between the two approaches
      It’ll be my next post on this blog 😀

  4. Philippe Beaudoin · November 27, 2010

    Just to reiterate: this approach doesn’t force you to use any specific container. You can write some of your tests with Jukito, some without if they end up being more readable this way. As mentioned briefly in the text, if you’re happy wiring everything by yourself you could just instantiate your TestBinder and pass it into your view’s constructor. This effectively frees your test of any dependence on Jukito or Guice.

    As for the tests presented here, they are not tied to a specific container. Yes, they are tied to Jukito, our testing framework, but it’s not worse in my opinion than being tied to JUnit or mockito. As Christian said, I’ve noticed Jukito makes my tests much smaller and easier to read. It also greatly facilitates wiring integration or semi-integrative tests. It’s basically taking the idea of guiceberry and making it work for tests at all scales.

    Under the hood, Jukito is using Guice to make things like automocking and autoinstantiation easier. It doesn’t have to, and I’m pretty sure it could be written on top of, say, PicoContainer. As a Jukito user, though, you shouldn’t have to care.

    As a container, Guice has a number of advantages:
    1) It has a nice DSL where you can do all the wiring in Java instead of XML. Jukito reuses this DSL as it works well in the context of small test classes. (Plus it allows sharing module code via inheritance or aggregation.)
    2) It supports almost 100% of Gin and shares its annotations. This shouldn’t be a problem once its all standardized in JSR330, but this standard is still a long way from being as flexible as what Guice and Gin allows…

  5. Fred · July 9, 2011

    public interface Binder extends UiBinder { }

    UiBinder now needs to be parameterized, doesn’t it?
    I knew you could!

    • Christian Goudreau · July 10, 2011

      You’re right, that should have been parameterized.

  6. Fred · July 10, 2011

    You’re rapidly proving to me that your slogan “… complete model-view-presenter framework to simplify your next GWT project.” is so much crap. Following is your entire example. ViewImpl doesn’t inherit from Widget, so initWidget() throws a syntax error.

    It’s obvious these comments will be modded out. Nevertheless they still stand.

    import com.google.gwt.uibinder.client.UiBinder;
    import com.google.gwt.uibinder.client.UiTemplate;
    import com.google.gwt.user.client.ui.Widget;
    import com.google.inject.Inject;
    import com.gwtplatform.mvp.client.ViewImpl;

    public class HelloViewImpl extends ViewImpl implements HelloView {

    public interface Binder extends UiBinder {
    }

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

    @Override
    public Widget asWidget() {
    // TODO Auto-generated method stub
    return null;
    }
    }

    • Christian Goudreau · July 10, 2011

      Well, you may be harsh, but it’s a good comment that don’t need to be mod out.

      At the time when that article was written I don’t even know if IsWidget was out yet. One thing was sure, you couldn’t even add a IsWidget in an HTMLPanel inside UIBinder. I remember that I had written my own HtmlPanel where I was able to add my IsWidget. Another thing, and it’s still something that you can’t do right now unless using @UiField(provided = true) you can’t use Gin inside UiBinder.

      That being said, now I would probably change ViewImpl to implements isWidget. Also, even thought that article is mean’t to give users an example. It isn’t official documentation that we keep up to date, else it would have been directly on Gwt-Platform’s wiki.

  7. John Doe · August 19, 2012

    Well, it’s definitely an interesting way to test a view, but I have a question. Probably this test purports that the view is not really dumb and has some logic (sayHello() method in this case). Is it all right in general? Thanks in advance.

    Oh, and by the way the code samples here http://code.google.com/p/arcbees-hive/wiki/TestingViews have less errors.

    • Christian Goudreau · August 20, 2012

      You’re right, there shouldn’t be any logic in the view and nothing to test either when you’re following MVP with dumb views.

      You’ll find this useful when starting to migrate legacy code and/or creating small widgets that doesn’t need presenters. It let you test the view with a JUnit test instead of a GWTTestCase which is much slower.

      • John Doe · August 20, 2012

        Yep, GWTTestCase is definitely not the best choice. I’d also denote gwt-test-utils and simple mocking tests as good options.

  8. polyk · May 7, 2013


    @Inject
    public MyTestBinder(final MockitoMockFactory mockitoMockFactory) {
    super(Widget.class, mockitoMockFactory);
    }

    the “super” isn’t clear, and you should have a warning :” No enclosing instance of type HelloViewTest is available due to some intermediate constructor invocation”

    • polyk · May 8, 2013

      No it’s ok, sorry

  9. polyk · May 15, 2013

    1. In your test, the view is a mock but when using a quality platform like sonar, the test coverage is almost 0% because we’re not testing a class, it’s a mock. How can I handle this ?

    2. I can’t access the UI fields (uibinder) as described here : “…..view.firstNameTextBox…..”
    3. How can I do the same with a presenter ? and how about events fired by presenters ?

  10. Christian Goudreau · May 15, 2013

    1. That’s right, but I would argue that the important is to have your platform 100% tested, not to have a fancy report like test coverage that says so. For example, most of my test are Cucumber test. There’s no way that any test coverage will really see everything that is tested. BUT, its much more robust and it test more than unit test will ever be able to do.

    2. You need to setup the return value using mockito.

    3. The presenter is mocked, you have to emulate the events. MockingBinder is designed to UnitTest views, for more end to end tests, you may have to look at GWT Test Case or Cucumber.

    • polyk · May 16, 2013

      Thanks for your answers

  11. Ben · May 22, 2014

    FYI, the “well documented” link at the top is broken and appears to redirect to an adult manga site or something. Might want to fix that 🙂

    • Christian Goudreau · May 22, 2014

      LOL! We should definitely change that 😀

Leave a comment