I've just started getting my hands dirty with Objectify (
link) with the Google App Engine (
link) and finally started small with unit tests instead of writing a bunch of code that breaks due to 3rd party modules not doing what the documentation says they do, i.e. using Test Driven Development (
TDD) in practice. I came across a fair amount of out-dated tutorials (
link link), old documentation (
link), forum posts (
link) and Stack Overflow questions so it's a perfect candidate for a tutorial post. In this tutorial we are going to write a JUnit test for objectify-appengine with a persisted Car object with an out-of-place VIN of "Hello World".
First off, figuring out those nasty Maven dependencies was quite a task by itself. The objectify docs will say you only need objectify (
link) but you need the full compliment of Google App Engine dependencies (
link link) as well as a Java Persistence API (
JPA) implementation (we use Apache Open JPA here,
link).
<dependencies>
<!-- Objectify -->
<dependency>
<groupid>com.googlecode.objectify</groupid>
<artifactid>objectify</artifactid>
<version>4.0b3</version>
</dependency>
<!-- Open JPA -->
<dependency>
<groupid>org.apache.openjpa</groupid>
<artifactid>openjpa-all</artifactid>
<version>2.2.2</version>
</dependency>
<!-- GAE Dependencies -->
<dependency>
<groupid>com.google.appengine</groupid>
<artifactid>appengine-api-1.0-sdk</artifactid>
<version>${appengine.target.version}</version>
</dependency>
<dependency>
<groupid>com.google.appengine</groupid>
<artifactid>appengine-testing</artifactid>
<version>${appengine.target.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>com.google.appengine</groupid>
<artifactid>appengine-api-labs</artifactid>
<version>${appengine.target.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>com.google.appengine</groupid>
<artifactid>appengine-api-stubs</artifactid>
<version>${appengine.target.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
For the Plain Old Java Object (
POJO) to persist we are going with the Car class as in the Objectify documentation (
link), except they forgot about the @Entity annotation (
link).
package ...;
// imports included for your convenience
import javax.persistence.Transient;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
@Entity
public class Car {
@Id
Long id;
String vin;
int color;
@Transient
String doNotPersist;
static {
ObjectifyService.register(Car.class);
}
private Car() {
}
public Car(String vin, int color) {
this.vin = vin;
this.color = color;
}
}
Lastly we have the test class itself. Note that save() and load() is used in Objectify 4 API (
link) instead of put(...) and get(...) in the old API. These methods also support using Keys (
link) but I wasn't able to figure out how you got Keys at that time, so the example (modified from the Objectify documentation) uses IDs.
package ...;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
public class ObjectifyAdapterTest {
private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
new LocalDatastoreServiceTestConfig());
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Before
public void setUp() throws Exception {
helper.setUp();
}
@After
public void tearDown() throws Exception {
helper.tearDown();
}
@Test
public void test() {
final Objectify ofy = ObjectifyService.ofy();
// Simple create, note the cheesy use of Hello World
final Car porsche = new Car("Hello World", 3);
ofy.save().entities(porsche).now();
final Long id = porsche.id;
assert id != null; // id was autogenerated
// Get it back
final Car loadedPorsche = ofy.load().type(Car.class).id(id).now();
assert porsche.equals(loadedPorsche);
// Change some data and write it
porsche.color = 1;
ofy.save().entities(porsche).now();
// Delete it
ofy.delete().entity(porsche).now();
}
}
Some key points are the helper object standing in for the Google App Engine persistence mechanism and that you get the same object back that you saved. Enjoy your quick start with Objectify, TDD, Maven and the Google App Engine!