objectuser.blog(brain)

objectuser.getBlog().contains(anything)

  • a place for thoughts, explanations, etc.

    this is just a place to write things out so I can make sure i understand them, keep track of them, and to use them again as links, etc.
  • Subscribe

Archive for August 2nd, 2009

Google App Engine Testing with Spring

Posted by objectuser on August 2, 2009

I’ve finally gotten around to writing some “integration” tests on Google App Engine.  I distinguish unit tests from integration tests basically by the things that are exercised in the test.  For the purposes of this post, integration tests exercise the datastore.

The GAE docs talk about how to setup a test to use the datastore.  But also follow the instructions here until the docs are fully updated.

For this post, I’m using GAE 1.2.2, Eclipse 3.5, Spring 2.5.6 and the JUnit 4 implementation that comes with Eclipse 3.5.  I also have a separate test project.  This would likely not work out if I wanted to run the tests in the host environment.

First, we need some test setup.  I have a hierarchy, which follows the Google documentation above (except their setup is for JUnit 3).  At the top is LocalServiceTestCase:

public class LocalServiceTestCase extends TestCase {
  @Before
  public void setUpGaeEnvironment() throws Exception {
    ApiProxy.setEnvironmentForCurrentThread(new TestEnvironment());
    ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(".")) {});
  }
  @After
  public void tearDownGaeEnvironment() throws Exception {
    // not strictly necessary to null these out but there's no harm either
    ApiProxy.setDelegate(null);
    ApiProxy.setEnvironmentForCurrentThread(null);
  }
}

Note the @Before and @After annotations.  This class useful if you just need the API environment in your test.  My new tests use the datastore, however, and for that, I have extended LocalServiceTestCase with LocalDatastoreTestCase:

public class LocalDatastoreTestCase extends LocalServiceTestCase {
  @Before
  public void setUpDatastore() throws Exception {
    ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();
    proxy.setProperty(LocalDatastoreService.NO_STORAGE_PROPERTY, Boolean.TRUE.toString());
  }
  @After
  public void tearDownDatastore() throws Exception {
    ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();
    LocalDatastoreService datastoreService = (LocalDatastoreService) proxy.getService("datastore_v3");
    datastoreService.clearProfiles();
  }
}

This relies on the implementation of TestEnvironment, which I made out of the Google documentation with updates from the Google group link.

public class TestEnvironment implements ApiProxy.Environment {
  @Override
  public String getAppId() {
    return "Unit Tests";
  }
  @Override
  public String getVersionId() {
    return "1.0";
  }
  @Override
  public String getRequestNamespace() {
    return "";
  }
  @Override
  public String getAuthDomain() {
    throw new UnsupportedOperationException();
  }
  @Override
  public boolean isLoggedIn() {
    throw new UnsupportedOperationException();
  }
  @Override
  public String getEmail() {
    throw new UnsupportedOperationException();
  }
  @Override
  public boolean isAdmin() {
    throw new UnsupportedOperationException();
  }
  @Override
  public Map<String, Object> getAttributes() {
    return new HashMap<String, Object>();
  }
}

With all of that available for extension, the test can focus on testing the application code:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class })
@ContextConfiguration(locations = { "classpath:app-context.xml", "classpath:integration-context.xml",
"classpath:jdo-context.xml", "classpath:security-context.xml" })
public class UserDaoTest extends LocalDatastoreTestCase {
  @Autowired
  private UserDao userDao;
  @Before
  public void insertTestUserNamedFred() {
    ...
  }
  @Test
  public void findByName() {
    User user = userDao.findByName("fred");
    assertNotNull(user);
  }
}

Note the use of the @RunWith annotation that sets up the test to use an alternative test runner.  In this case, we’ll use Spring’s test runner, which allows the addition of the subsequent annotations.

Next, Spring’s @TestExecutionListeners comes into play in order to get dependency injection to work using DependencyInjectionTestExecutionListener.

Finally, we tell the test where to find the context configurations.  The @ContextConfiguration annotation references configurations in the classpath.  I currently have the directory containing these configurations added to the classpath of my test project.

Run that using JUnit 4 and the autowiring should take effect, wiring up everything you need to test your class.  Too bad autowiring doesn’t work on the server.  So if you need to run your tests on the server, user setter injection.

That should be all you need.  Hope this helps.

Some other things that could be done include a base class with all of those annotations and a context configuration that didn’t require each configuration file to be listed (I don’t even know if that’s possible, however).

Posted in Google App Engine | 11 Comments »

 
Design a site like this with WordPress.com
Get started