I wrap the Ref in a Guava Supplier to avoid the dependency on Objectify during unit testing of my pojos. The suppliers are translated to a datastore Key in a similar manner as Ref.
This class is mostly copied from the Objectify RefTranslatorFactory:
public class RefSupplierTranslatorFactory
extends ValueTranslatorFactory<Supplier<?>, com.google.appengine.api.datastore.Key> {
@SuppressWarnings({ "unchecked", "rawtypes" })
public RefSupplierTranslatorFactory() {
super((Class) Supplier.class);
}
@Override
protected ValueTranslator<Supplier<?>, com.google.appengine.api.datastore.Key> createValueTranslator(
TypeKey<Supplier<?>> tk, CreateContext ctx, Path path) {
final LoadConditions loadConditions = new LoadConditions(tk.getAnnotation(Load.class));
return new ValueTranslator<Supplier<?>, com.google.appengine.api.datastore.Key>(
com.google.appengine.api.datastore.Key.class) {
@Override
protected Supplier<?> loadValue(com.google.appengine.api.datastore.Key value, LoadContext ctx, Path path)
throws SkipException {
Ref<Object> ref = ctx.loadRef(Key.create(value), loadConditions);
return new RefSupplier(ref);
}
@Override
protected com.google.appengine.api.datastore.Key saveValue(Supplier<?> value, boolean index,
SaveContext ctx, Path path) throws SkipException {
return ctx.saveRef(Ref.create(value.get()), loadConditions);
}
};
}
public static class RefSupplier
implements Serializable, Supplier<Object> {
private static final long serialVersionUID = 1L;
final private Ref<?> ref;
public RefSupplier(Ref<?> ref) {
this.ref = ref;
}
@Override
public Object get() {
return ref.get();
}
}
}
Say I have the following Pojos:
@Entity
public static class CarWithSupplier {
@Id
Long id;
Supplier<SteeringWheel> steeringWheel;
List<Supplier<Tire>> tires;
}
@Entity
public static class SteeringWheel {
@Id
Long id;
}
@Entity
public static class Tire {
@Id
Long id;
}
I am able to run a unit test without dependency on Objectify:
@Test
public void testSupplier() {
CarWithSupplier car = carWithSupplier();
assertNotNull(car.steeringWheel);
assertNotNull(car.tires);
assertEquals(2, car.tires.size());
}
protected CarWithSupplier carWithSupplier() {
CarWithSupplier car = new CarWithSupplier();
car.steeringWheel = Suppliers.ofInstance(steeringWheel());
final Supplier<Tire> leftFrontTire = Suppliers.ofInstance(tire());
final Supplier<Tire> rightFrontTire = Suppliers.ofInstance(tire());
car.tires = ImmutableList.of(leftFrontTire, rightFrontTire);
return car;
}
Extending the unit test, but setting up the necessary objectify resources during test setup I am able to get the same unit test to run against the datastore:
@Before
public void setUpObjectify() throws Exception {
helper.setUp();
closeable = ObjectifyService.begin();
final ObjectifyFactory factory = ObjectifyService.factory();
factory.getTranslators().add(new RefSupplierTranslatorFactory());
factory.register(CarWithSupplier.class);
factory.register(SteeringWheel.class);
factory.register(Tire.class);
}
@Override
protected CarWithSupplier carWithSupplier() {
final CarWithSupplier car = super.carWithSupplier();
final Objectify ofy = ObjectifyService.ofy();
Key<CarWithSupplier> key = ofy.save().entity(car).now();
return ofy.load().key(key).now();
}
@Override
protected Tire tire() {
final Tire tire = super.tire();
ObjectifyService.ofy().save().entity(tire).now();
return tire;
}
@Override
protected SteeringWheel steeringWheel() {
final SteeringWheel steeringWheel = super.steeringWheel();
ObjectifyService.ofy().save().entity(steeringWheel).now();
return steeringWheel;
}
Unit testing of my pojos is valuable since they are populated initially using the JSON response from third party web API services (using Gson). I find it valuable to separate testing of Gson parsing from testing objectify datastore function. I later test them altogether during integration testing.
I haven't put this through extensive use yet so I welcome input from @stickfigure if this may cause issues or otherwise take away advantages of using Ref directly.