TurboManage

David Chandler’s Journal of Java Web Development

  • David M. Chandler

    15-yr veteran of Web apps residing in Atlanta with the wife of my youth and our five children. My current project is ROA, a prayer list keeper written in GWT for AppEngine. In my "spare" time, I take pictures, preferably of Rocky Mountain National Park like the one above in which I am waving from The Keyhole.

  • My Google Notebooks

  • Blog Stats

    • 38,473 hits

CloudCamp Atlanta tonight

Posted by David Chandler on October 28, 2009

I’m 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 Atlanta.

I’m looking forward to meeting some local AppEngine developers, as I’ve been feeling awfully close to the bleeding edge lately. I routinely find that solutions have been posted on AppEngine forums just eight days ago, and sometimes don’t exist yet. I really need TaskQueue in order to send out emails, which is still experimental in Labs, and would even more like to use deferred.defer (less than two weeks old), but alas, it’s currently only available for Python.

Posted in AppEngine | Leave a Comment »

Writing common test data services for gwt-dispatch with Guice

Posted by David Chandler on October 28, 2009

The way it stands now after my last several posts on unit testing is that each test method in a JUnit TestCase runs its own context, including its own PersistenceManager injected with Guice. Because I’m initializing the AppEngine test environment with LocalDatastoreService.NO_STORAGE_PROPERTY = TRUE, any test data I create in one test is not available to the next test method, even in the same TestCase class. This is typical behavior for JUnit test cases, and generally a good thing as each test should be independent, but it does mean we need a convenient way to create test data. My current strategy is twofold:

  1. Create test data used by all tests in the TestCase setUp() method that runs before each test
  2. Create common test data (used by multiple TestCases) in test data generator services

The first problem to solve is how to get access to a dispatch service and PersistenceManager inside the test data generators. Both are now being injected by Guice as covered in previous posts, so I’ve written a BaseTestDataService that does the Guice magic:

package com.roa.test.service;

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

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.roa.server.guice.ServerModule;
import com.turbomanage.gwt.server.PMF;
import com.turbomanage.gwt.server.guice.DispatchTestModule;

public class BaseTestDataService
{
	protected static Injector inj = Guice.createInjector(new ServerModule(),
		new DispatchTestModule());;

	protected static StandardDispatchService getService()
	{
		return inj.getInstance(StandardDispatchService.class);
	}

	protected static PMF getPMF()
	{
		return inj.getInstance(PMF.class);
	}
}

I’m intentionally using a static getPMF() method to make it as simple as possible to use the test data services in a unit test via static methods. Alternatively, I could have used instance methods and constructor injection, but then you’d have to create a new instance of a test data service in each test, which is just that much more code to write… Also, constructor injection is not possible in this case because the TestCases themselves are not instantiated by Guice, so instantiating a new test data service from a test case would not involve Guice, either.

It does not matter that the BaseTestDataService and BaseTest (below) are both calling Guice.createInjector() and thereby creating multiple Guice contexts, each having its own PMF instance. The important thing for these database tests is that they’re all going against one Datastore, not one PersistenceManager.

Here’s a simple test data generator that extends BaseTestDataService and provides a method to add a test user:

package com.roa.test.service;

import com.roa.client.domain.User;
import com.roa.shared.rpc.AddUserAction;
import com.roa.shared.rpc.AddUserResult;

public class UserTestDataService extends BaseTestDataService
{
	public static User addTestUser() 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) getService().execute(
			new AddUserAction(u));
		u = userResult.getUser();
		return u;
	}
}

Note the call to getService() on line 17. Thanks to Guice, test data generator services can invoke gwt-dispatch ActionHandlers the same way as the tests themselves.
Finally, here’s a test case that calls the above service to create a test user in the Datastore:

package com.roa.test;

import java.util.List;

import javax.jdo.Query;

import com.appenginefan.toolkit.unittests.BaseTest;
import com.roa.client.domain.User;
import com.roa.test.service.UserTestDataService;

public class UserTestCase extends BaseTest
{

	@Override
	protected void setUp() throws Exception
	{
		super.setUp();
		UserTestDataService.addTestUser();
	}

	public void testUserAdded() throws Exception
	{
		// Run tests here
		Query q = pm.newQuery(User.class, "emailAddress == e");
		q.declareParameters("java.lang.String e");
		List<User> users = (List<com.roa.client.domain.User>) q.execute("test@example.com");
		assertEquals(1, users.size());
		User user = users.get(0);
		assertNotNull(user.getId());
	}
}

I should probably show my BaseTest method, too. I’m using AppEngineFan’s BaseTest as discussed in previous posts and modified the setUp() method for Guice injection as follows:

	/**
	 * Sets up the App Engine environment.
	 */
	@Override
	protected void setUp() throws Exception
	{
		if (initializer != null)
		{
			throw new UnsupportedOperationException(
				"setup may only be called once!");
		}
		super.setUp();
		initializer = new TestInitializer(getEnvironmentOrNull());
		initializer.setUp();
		Injector inj = Guice.createInjector(new ServerModule(),
			new DispatchTestModule());
		this.testSvc = inj.getInstance(StandardDispatchService.class);
		this.pm = inj.getInstance(PMF.class).getPersistenceManager();
	}

Thanks to Guice, we can now get access to the PersistenceManager and call ActionHandlers via a dispatch test service even from static methods in test data generators. This greatly streamlines unit testing.

Posted in AppEngine, Model-View-Presenter | Leave a Comment »

More on unit testing with an injected JDO PersistenceManager

Posted by David Chandler on October 27, 2009

Regarding my previous post, it turns out that I needed a TestPMF implementation sooner than I thought. The reason is the way I’m injecting a DispatchTestService in my unit tests (as described in this post). I’m calling createInjector() in the setUp() method, which gets run before each test method:

	@Override
	protected void setUp() throws Exception
	{
		super.setUp();
		Injector inj = Guice.createInjector(new ServerModule(),
			new DispatchTestModule());
		testSvc = inj.getInstance(StandardDispatchService.class);
		// This pm is needed only for code in this class that calls a PM directly
		// ActionHandlers are injected with the PMF singleton from Guice
		pm = inj.getInstance(PMF.class).getPersistenceManager();
	}

Since setUp() gets called before each test method, Guice is initialized for each test method, and therefore Guice calls the constructor for my DefaultPMF class for each test method. Repeated from the previous post, the DefaultPMF class looks like:

package com.turbomanage.gwt.server;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;

public final class DefaultPMF implements com.turbomanage.gwt.server.PMF
{
	private final PersistenceManagerFactory pmfInstance = JDOHelper
			.getPersistenceManagerFactory("transactions-optional");

	public DefaultPMF()
	{
	}

	@Override
	public PersistenceManager getPersistenceManager()
	{
		return pmfInstance.getPersistenceManager();
	}
}

Because DefaultPMF creates a named PersistenceManagerFactory, JDO complains that the named PersistenceManagerFactory has already been created. My solution for now is to replace the DefaultPMF with a TestPMF that uses a Properties map to initialize the PersistenceManager, just as the AppEngineFan TestInitializer does. Here’s a working TestPMF:

package com.turbomanage.gwt.server;

import java.util.Properties;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;

public class TestPMF implements PMF
{
	private final PersistenceManagerFactory pmf;

	public TestPMF()
	{
		Properties newProperties = new Properties();
		newProperties
			.put("javax.jdo.PersistenceManagerFactoryClass",
				"org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory");
		newProperties.put("javax.jdo.option.ConnectionURL", "appengine");
		newProperties.put("javax.jdo.option.NontransactionalRead", "true");
		newProperties.put("javax.jdo.option.NontransactionalWrite", "true");
		newProperties.put("javax.jdo.option.RetainValues", "true");
		newProperties.put("datanucleus.appengine.autoCreateDatastoreTxns",
			"true");
		newProperties.put("datanucleus.appengine.autoCreateDatastoreTxns",
			"true");
		pmf = JDOHelper.getPersistenceManagerFactory(newProperties);
	}

	@Override
	public PersistenceManager getPersistenceManager()
	{
		return pmf.getPersistenceManager();
	}

}

Now simply bind PMF to its TestPMF implementation in your Guice module for tests (DispatchTestModule in the setUp() method above), and each unit test method will run with a freshly created PersistenceManager.

Posted in AppEngine, Model-View-Presenter | 1 Comment »

Unit testing with JDO PersistenceManager injected via Guice

Posted 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 PersistenceManager into my ActionHandlers in order to provide an alternate PersistenceManager for unit testing. I don’t actually need an alternate PM yet as it is handled transparently by my AppEngine test environment, but I thought it would be easier to do it sooner rather than later, so here goes.

I’ve created an interface called PMF (in order to avoid confusion with JDO’s PersistenceManagerFactory).

package com.turbomanage.gwt.server;

import javax.jdo.PersistenceManager;

public interface PMF
{
	PersistenceManager getPersistenceManager();
}

My default PMF implementation works exactly the same as before:

package com.turbomanage.gwt.server;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;

public final class DefaultPMF implements com.turbomanage.gwt.server.PMF
{
	private final PersistenceManagerFactory pmfInstance = JDOHelper
			.getPersistenceManagerFactory("transactions-optional");

	public DefaultPMF()
	{
	}

	@Override
	public PersistenceManager getPersistenceManager()
	{
		return pmfInstance.getPersistenceManager();
	}
}

The DefaultPMF gets bound in my Guice ServerModule:

	@Override
	protected void configureHandlers()
	{
		...
		bind(PMF.class).to(DefaultPMF.class).in(Singleton.class);
		...
	}

And finally, the PMF is injected into an ActionHandler:

public class AddUserHandler implements
	ActionHandler<AddUserAction, AddUserResult>
{
	@Inject
	private PMF pmf;
	private PersistenceManager pm;

	@Override
	public AddUserResult execute(AddUserAction action, ExecutionContext context)
		throws ActionException
	{
		this.pm = pmf.getPersistenceManager();
		...
	}
	...
}

Now, when the need arises for a test implementation of PMF, I can easily bind a different implementation in my Guice TestModule as shown in the earlier post.

Update: I did not test this thoroughly before I posted, and the need for a TestPMF arose just hours later. See my next post for the solution.

Posted in AppEngine, Model-View-Presenter | Leave a Comment »

Run Linux and Windows together without dual booting

Posted by David Chandler on October 24, 2009

Welcome to “the Saturday evening post.” This will be a more-or-less weekly PC tech post in addition to my daily Java-related postings throughout the week.

There are two programs I can’t live without under Linux: Quicken, which I first installed on 3.5 inch floppies and has run my financial life ever since, and RoboForm, which manages nearly 100 mostly-generated passwords for me. As much as I would like to run Ubuntu and OpenOffice exclusively, I am unwilling to dedicate another PC to the task or to be constantly rebooting.

I was therefore delighted to find that you can run Ubuntu right within Windows as a virtual machine using the free VMware Player. Here are the steps to install Ubuntu on a clean virtual machine.

  1. Download and install VMware Player
  2. Download the Ubuntu ISO image
  3. Go to easyvmx.com and fill in the blanks to create your virtual machine definition file. Check the LiveCD ISO-image box and enter the path to the downloaded ISO file. When you run VMware Player for the first time, it will boot from the ISO image just as if you had put the CD in the drive, allowing you to run or install Ubuntu in the virtual machine.

That’s all there is to it. Granted, with this approach, you still have to deal with Windows, but being able to switch back and forth between operating systems with Alt+Tab sure is handy.

A very cool thing about virtual machines is that all the data for the VM exists in a few files under your VMX directory. Which means you can copy the VMX files to a portable hard drive, plug it into any computer with VMware Player installed, and voila, you’re right where you left off. That is one painless backup strategy.

Theoretically, you can also run the other way around, by the way, and install Windows in a virtual machine running under VMware Player for Linux. However, I have not tried this.

Posted in PC Tech | Leave a Comment »

JSF Quick Reference available again

Posted by David Chandler on October 23, 2009

At long last, I’ve resurrected the JSF quick reference that I used to host at learnjsf.com. It’s on the JSF page above.

Posted in JavaServer Faces | Leave a Comment »

How to show a “Loading…” pop-up in your GWT app

Posted by David Chandler on October 22, 2009

Last week, I posted a technique for displaying a splash screen before GWT loads. In this post, we’ll look at a way to show a modal pop-up after your app is up and running, but while GWT is making AJAX calls.

The basic idea is to show and center a pop-up with some text and an Ajax wait image. There are a couple ways to hook it into gwt-presenter. The WidgetDisplay interface method has startProcessing() and stopProcessing() methods that get called by the DisplayCallback class that you pass to dispatch.execute(). If you want to show the pop-up for only one presenter, then you can simply implement these methods in your view. More likely, however, a centered pop-up will be used by multiple presenters, in which case we can create an AppLoadingPresenter and AppLoadingView that will listen for an AppLoadingEvent. Any presenter or service can then fire the AppLoadingEvent to cause the “Loading…” panel to be shown. Here’s some very elegant code courtesy of my co-worker Tony Richardson. I’ll leave it up to you to create the AppLoadingEvent and fire where you need it. One more note: I’m reusing the AppLoadingEvent to trigger both showing and hiding the pop-up by means of a boolean argument in the AppLoadingEvent constructor. It might be more correct to use separate events for this, but it’s getting late…

package com.roa.client.presenter;

import java.util.List;

import net.customware.gwt.presenter.client.EventBus;
import net.customware.gwt.presenter.client.place.Place;
import net.customware.gwt.presenter.client.widget.WidgetDisplay;
import net.customware.gwt.presenter.client.widget.WidgetPresenter;

import com.google.inject.Inject;
import com.roa.client.event.AppLoadingEvent;
import com.roa.client.handler.AppLoadingEventHandler;

public final class AppLoadingPresenter extends
        WidgetPresenter<AppLoadingPresenter.Display>
{
    public interface Display extends WidgetDisplay
    {
        void showWidget();
    }

    private final Display display;
    private final EventBus eventBus;

    @Inject
    public AppLoadingPresenter(Display display, EventBus eventBus)
    {
        super(display, eventBus);
        this.display = display;
        this.eventBus = eventBus;
        bind();
    }

    @Override
    public Place getPlace()
    {
        // We won't really use this presenter as a Place
        return null;
    }

    public void hideLoading()
    {
        this.display.startProcessing();
    }

    @Override
    protected void onBind()
    {
        registerHandler(this.eventBus.addHandler(AppLoadingEvent.TYPE, new AppLoadingEventHandler()
		{
			@Override
			public void onAppLoadingEvent(boolean isComplete)
			{
				if (isComplete)
				{
					display.stopProcessing();
				}
				else
				{
					display.startProcessing();
				}
			}
		}));
    }

    @Override
    public void revealDisplay()
    {
        display.showWidget();
    }
}

And here’s the AppLoadingView. Very simple, but cool effect.

package com.roa.client.ui.web;

import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.roa.client.presenter.AppLoadingPresenter.Display;

public final class AppLoadingView extends PopupPanel implements Display
{
    private final FlowPanel container = new FlowPanel();

    public AppLoadingView()
    {
        final Image ajaxImage = new Image("path_to_ajax_wait_image");
        final Grid grid = new Grid(1, 2);
        grid.setWidget(0, 0, ajaxImage);
        grid.setText(0, 1, "Loading...");
        this.container.add(grid);
        add(this.container);
    }

    @Override
    public Widget asWidget()
    {
        return this;
    }

    @Override
    public void stopProcessing()
    {
        hide();
    }

    @Override
    public void startProcessing()
    {
        center();
        show();
    }

    @Override
    public void showWidget()
    {
        startProcessing();
    }
}

Posted in Google Web Toolkit, Model-View-Presenter | Leave a Comment »

Swapping DIVs technique for navigation with gwt-presenter

Posted by David Chandler on October 21, 2009

I have previously written about navigation in gwt-presenter using the DeckPresenter or WidgetContainerPresenter approaches. Those techniques work well for swapping out views in one portion of the page, and could be applied to the whole page, as well; however, here’s another simpler technique for situations in which you need to swap between totally unrelated views. For example, my main app has a header, footer, and content area in which views are swapped out using a WidgetContainerPresenter. However, I also need to replace the whole page (header, footer, and all) for a user registration page. To do this, I simply created two DIVs in the HTML host page:

		<div id="roa-registration"></div>
		<div id="container"></div>

My main AppPresenter loads all the presenters up front and fires a PlaceRequestEvent to the user registration presenter for new users. For existing users, it fires a PlaceRequestEvent based on the URL or to the default presenter if the URL is empty.

...
	public void go(final HasWidgets container)
	{
		this.container = container;
		loadMain();
		hideSplash();
		if (roaModel.getLoginInfo().getUser() != null)
		{
			showMain();
			String currentPlace = History.getToken();
			if ("".equals(currentPlace))
			{
				goToDefaultView();
			}
			else
			{
				// Load requested page
				placeManager.fireCurrentPlace();
			}
		}
		else
		{
			showRegistration();
		}
	}

	private void showRegistration()
	{
		// New user must register
		eventBus.fireEvent(new PlaceRequestEvent(new PlaceRequest(
			UserRegistrationPresenter.PLACE)));
	}

	private void hideSplash()
	{
		DOM.removeChild(RootPanel.getBodyElement(), DOM
			.getElementById(RoaStyles.ID_SPLASH));
	}

	private void loadMain()
	{
		// Hide while loading
		 DOM.setStyleAttribute(RootPanel.get(RoaStyles.CONTAINER_ID)
		 .getElement(), "display", "none");
		container.clear();
		HeaderPanel headerPanel = new HeaderPanel(roaModel.getLoginInfo());
		headerPanel.add(messagePresenter.getDisplay().asWidget());
		container.add(headerPanel);
		...
		container.add(bodyPanel);
		container.add(new FooterPanel());
	}

	private void showMain()
	{
		// Set GWT container visible
		DOM.setStyleAttribute(RootPanel.get(RoaStyles.CONTAINER_ID)
			.getElement(), "display", "block");
		// Load initial data
		prayerListService.refreshPrayerLists();
	}

	private void goToDefaultView()
	{
		// Nothing in URL, load default page
		eventBus.fireEvent(new PlaceRequestEvent(new PlaceRequest(
			ManageListsPresenter.PLACE)));
	}

Finally, I toggle the visibility of the two DIVs in the user registration presenter’s revealDisplay() method, which gets called in response to a PlaceRequestEvent. In addition, I call a hideDisplay() method in the presenter’s onBind() method (called from the constructor), so that its initial state is hidden.

	@Override
	public void revealDisplay()
	{
		super.revealDisplay();
		RootPanel.get(RoaStyles.CONTAINER_ID).setVisible(false);
		RootPanel.get(RoaStyles.REGISTRATION_CONTAINER_ID).setVisible(true);
	}

	public void hideDisplay()
	{
		RootPanel.get(RoaStyles.REGISTRATION_CONTAINER_ID).setVisible(false);
	}

The downside to this technique is that it would be a pain to manage with more than a few DIVs, as each presenter would have to know how to show itself and hide the others; however, for swapping between completely different page layouts, I think it’s easier than creating nested WidgetContainerPresenters (if that would even work). On the plus side, multiple presenters can use each layout (DIV), and most applications are likely to need only a handful of completely distinct page layouts. YMMV.

Posted in Google Web Toolkit, Model-View-Presenter | 7 Comments »

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.

Posted in AppEngine, Google Web Toolkit | 2 Comments »

Unit testing the AppEngine Datastore with JDO

Posted by David Chandler on October 19, 2009

I’ll admit, I was lazy at first and didn’t write server-side unit tests for my JDO persistence methods. I was effectively doing testing via trial and error from the client, which has two drawbacks: 1) if you make a change to server-side code, you have to restart the hosted development environment, which takes a while, and 2) since GWT doesn’t know about all of JDO’s exceptions, JDO errors in server-side code are frequently masked by GWT serialization errors involving the JDOFatalUserException or some such.

Google provides a recipe for AppEngine unit testing, but their coverage of testing the Datastore is incomplete. Thankfully, AppEngineFan has solved this and published a blog post and some nice code to help test AppEngine JDO calls. All I needed was the three classes in the com.appenginefan.toolkit.unittests package. Just extend BaseTest, call the newPersistenceManager() method, and you’re off and running.

Note: this post applies to testing only in the local development environment. I doubt you can run unit tests in the AppEngine production environment, but if I’m wrong about that, please post a comment as I’d sure like to know about it.

Posted in AppEngine, Google Web Toolkit | 1 Comment »