TurboManage

David Chandler's Journal of Java Web and Mobile Development

How to unit test gwt-dispatch ActionHandlers with Guice

Posted by David Chandler on October 20, 2009

Here’s an easy recipe for running server-side unit tests against your ActionHandlers. Thanks to Guice, you can simply replace the real dispatch servlet with a DispatchTestService that you call from your unit tests. You’ll pass it an Action and get back a Result just the same as you would from the client. Here’s a simple TestCase (note: I’m extending the BaseTest from AppEngineFan that I mentioned in my previous post in order to get access to a test AppEngine environment).

package com.roa.test;

import net.customware.gwt.dispatch.client.standard.StandardDispatchService;

import com.appenginefan.toolkit.unittests.BaseTest;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.roa.client.domain.User;
import com.roa.server.guice.ServerModule;
import com.roa.shared.rpc.AddUserAction;
import com.roa.shared.rpc.AddUserResult;

public class AddUserTestCase extends BaseTest
{
	private StandardDispatchService testSvc;

	@Override
	protected void setUp() throws Exception
	{
		super.setUp();
		Injector inj = Guice.createInjector(new ServerModule(),
			new DispatchTestModule());
		testSvc = inj.getInstance(StandardDispatchService.class);
	}

	public void testAddUser() throws Exception
	{
		// Create new user
		User u = new User();
		u.setEmailAddress("test@example.com");
		u.setFirstName("Test");
		u.setLastName("User");
		u.setGoogleAccountId("testAccountId");
		AddUserResult userResult = (AddUserResult) testSvc.execute(
			new AddUserAction(u));
		u = userResult.getUser();
	}
}

The Guice injector in the setUp method uses our real ServerModule, which maps each Action to its ActionHandler. The DispatchTestModule simply binds the gwt-dispatch StandardDispatchService to a test implementation. Here’s the DispatchTestModule:

package com.roa.test;

import net.customware.gwt.dispatch.client.standard.StandardDispatchService;

import com.google.inject.AbstractModule;
import com.google.inject.Singleton;

public class DispatchTestModule extends AbstractModule
{
	@Override
	protected void configure()
	{
		bind(StandardDispatchService.class).to(DispatchTestService.class).in(
			Singleton.class);
	}
}

And our DispatchTestService:

package com.roa.test;

import net.customware.gwt.dispatch.client.standard.StandardDispatchService;
import net.customware.gwt.dispatch.server.Dispatch;
import net.customware.gwt.dispatch.shared.Action;
import net.customware.gwt.dispatch.shared.Result;

import com.google.inject.Inject;

public class DispatchTestService implements StandardDispatchService
{
	private Dispatch dispatch;

	@Inject
	public DispatchTestService(Dispatch dispatch)
	{
		this.dispatch = dispatch;
	}

	@Override
	public Result execute(Action<?> action) throws Exception
	{
		Result result = dispatch.execute(action);
		return result;
	}
}

Several of my ActionHandlers call the AppEngine UserService, so I’ve modified AppEngineFan’s TestEnvironment class to supply test values:

...
  public String getEmail() {
//    throw new UnsupportedOperationException();
    return "test@example.com";
  }

  public boolean isLoggedIn() {
//    throw new UnsupportedOperationException();
    return true;
  }

  public String getAuthDomain() {
//    throw new UnsupportedOperationException();
    return "test";
  }
...

I’m using the StandardDispatchService in unit tests in place of the AppEngineDispatchService I wrote about in a previous post because there’s no point in passing the extra session ID parameter with each call to dispatch.execute(). This is yet another benefit to using gwt-dispatch: all my service logic resides in ActionHandlers and can therefore be tested without so much as a mock servlet.

To get to the AppEngine Datastore, my ActionHandlers are using a PersistenceManagerFactory singleton that calls JDOHelper.getPersistenceManagerFactory(). Thankfully, this seems to work just fine in the tests, and I haven’t (yet?) had a need to inject different instances of a PersistenceManager in test vs. main code. See the previous post for a reference to the AppEngineFan code that initializes the AppEngine test environment, including Datastore. Note that the AppEngineFan TestInitializer sets the Datastore service NO_STORAGE_PROPERTY to true. This means that each test method in your TestCase starts with an empty Datastore, so each method needs to begin by populating the data it needs for the test.

4 Responses to “How to unit test gwt-dispatch ActionHandlers with Guice”

  1. […] by David Chandler on October 26, 2009 This is a follow-up to last week’s post on unit testing ActionHandlers with Guice. David Peterson pointed out on the gwt-dispatch mailing list that I could inject a […]

  2. […] planning to do a short unconference session on securing AppEngine services with gwt-dispatch and unit testing with AppEngine and gwt-dispatch at tonight’s CloudCamp […]

  3. Dan Billings said

    I don’t understand why you are using a DispatchTestService. It seems to me like you could just use a Dispatch and issue actions to it, right?

    • Excellent question. I was thinking in terms of simulating an RPC call at its outermost layer by replacing the StandardDispatchServiceServlet, which implements the StandardDispatchService, so I created an alternate implementation of the interface in DispatchTestService. As you’ve observed, it is not strictly necessary to wrap Dispatch this way, but could be used to simulate authentication or other things that the servlet might do.

Leave a comment