JAW Speak

Jonathan Andrew Wolter

Fast and Easily Testable GWT JUnit Tests

without comments

Reading time: 4 – 6 minutes

The principle we were trying to follow last November during a short Google Web Toolkit project was to test our controllers as much as possible outside of GWTTestCase. (It’s too slow). So that means never instantiate or reference a GWT widget in the controller. They must always be decoupled by interfaces. Here’s some of what we learned. Formerly my controller had an async event handler in it that showed a pop up message on a failure.
private AsyncCallback asyncCallback = new AsyncCallback() {
  public void onFailure(Throwable caught) {
    Window.alert("An error occured, please try again. \n" + caught.getMessage());
  }
  public void onSuccess(PackageDto pkg) {
    firePackageUpdate(pkg); // throw an event in here that is handled by a
                            // listener, which is the gui code and which has no logic
                            // (logic is in the controller!)
  }
};
But since I want to run my controllers with regular JUnit, I can’t have the Window.alert call in there. If I did and ran a junit test, it would get this stack trace:
java.lang.ExceptionInInitializerError
Caused by: java.lang.UnsupportedOperationException: ERROR: GWT.create() is only usable in client code!
   It cannot be called, for example, from server code.  If you are running a unit test, check that your test
   case extends GWTTestCase and that GWT.create() is not called from within an initializer or constructor.
at com.google.gwt.core.client.GWT.create(GWT.java:91)
at com.google.gwt.user.client.Window.(Window.java:230)
... 28 more
I saw two initial solutions to removing the Window.alert(), and then the third recommended solution:
  1. Implement the AsyncCallback<T> interface with BaseAsyncCallback<T> , and implement the onFailure() to call Window.alert(). In tests, I would subclass and override onFailure() in tests, preventing the Window.alert() from executing. I generally don’t like subclassing for tests, so I wasn’t too keen on this.
  2. Implement the AsyncCallback<T> interface with a class BaseAsyncCallback<T>, but this time take in a constructor parameter, FailureHandler (which would be an interface or class of my own.) Then in onFailure(), delegate to the FailureHandler field. In tests, pass a different FailureHandler implementation or subclass, to avoid calling the GWT widget code.
  3. The recommended solution is to treat this the same way I deal with onSuccess(). Tell a listener that a failure event occurred, and let it handle it.
What is key is the notifyAboutFailure() method takes a String of the message to the user (possibly using internationalization along the way). So anything interesting (error processing) still happens on the Controller side. Right now our view can handle the error by calling Window.alert(), but later when we implement the nicer “flash or gmail yellow bar” style UI, that can be dropped in (and tested with GWTTestCase or Selenium).
Principle to remember:
  • Separate the Controllers from all references to Views (GWT UI objects) by using event listener / notifier interfaces.
You also should read:

Bookmark and Share

Written by Jonathan

July 30th, 2009 at 1:05 am

Posted in code, java, testability

Leave a Reply