Search code examples
google-app-enginejunit4objectify

AppEngine Objectify Java Unit Test Object Cache Error


AppEngine Objectify Java Unit Test gives a weird session cache error as described below.

Test Case:

private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
        new LocalDatastoreServiceTestConfig());

protected Closeable session;

@Before
public void setUp() throws Exception {        
    helper.setUp();

    ObjectifyService.setFactory(new ObjectifyFactory());
    ObjectifyService.register(UserData.class);
    session = ObjectifyService.begin();
}

@After
public void tearDown() throws Exception {
    session.close();
    helper.tearDown();
}

@Test
public void testUserDataQuery throws Exception {
    ...
    saveUserData();
    ...
    getUserData();
    ...
}

Call 1:

UserData saveUserData {

    ...
    UserData userData = (UserData) ofy().load().key(key).now();
    ...

    // UserData is modified, the modifications are not stored in datastore,
    // as those are temporary.

    return userData;
}

Call 2:

UserData getUserData {

    ...
    UserData userData = (UserData) ofy().load().key(key).now();
    ...

    // Return the datastore saved UserData object.
    return userData;
}

When the unit test case is executed, the modification done in saveUserData call is seen in getUserData query. Even though ofy().load() has been called, the UserData is not loaded from datastore instead served from the cached entry.

I have tried ofy().clear() call to clear the session cache. This is not avoid the error in all the cases.

This is happening only in Unit Test environment rather than in development or production server.


Solution

  • In the code you posted, yes you'll get the same object back - that's the way the session cache works. Clearing the session cache after save will indeed give you a new object loaded from the datastore (or memcache). But my guess is that this is not what you really want to test.

    I'm wildly guessing that you are trying to simulate multiple backend calls in your test. IRL, each backend call will operate in its own server-side context. So what I recommend is creating a context for each call, using closures:

    @Test
    public void testUserDataQuery throws Exception {
        ...
        req(() -> saveUserData());
        ...
        req(() -> getUserData());
        ...
    }
    

    Where req() does the begin()/close() of the Objectify context (as well as any other request-specific processing your container usually does). You can leave the Objectify initialization in the before/after.