我不确定我是否应该全力以赴而不是这样做,但我通常习惯于模拟我的应用程序的许多区域,当我在实体中使用Ref和Keys时会遇到问题。
我遵循了docs中的模式,在实体中使用引用,但您屏蔽了引用,以便可以访问它引用的驱动程序对象,如下所示:
@Entity
class Car {
@Id Long id;
@Load Ref<Person> driver; // Person is an @Entity
public Person getDriver() { return driver.get(); }
public void setDriver(Person value) { driver = Ref.create(value); }
}
我的特定场景是使用一个导入器,在那里我正在解析xml并构建准备保存它们的实体。因为我的单元测试实际上只是测试xml的导入和解析,所以我模拟了我的dao实现,所以我实际上并没有使用数据存储。
然后我遇到了问题,当我调用setDriver时,它只是创建了一个引用对象。然后,我使用getDriver方法,该方法将返回null,因为Ref.get直接依赖于数据存储。
有没有人遇到过这个问题,有没有办法创建一个模拟引用对象?我正在考虑在我的实体中不直接引用Ref,而是引用一个可以提供我在测试中控制的Ref的帮助器类?
发布于 2015-06-03 15:48:01
我将Ref包装在一个Guava供应商中,以避免在pojos的单元测试期间对Objectify的依赖。以与Ref类似的方式将供应商转换为数据存储关键字。
此类主要从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();
}
}
}
假设我有以下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;
}
我可以在不依赖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;
}
扩展单元测试,但在测试设置期间设置必要的对象化资源,我能够获得对数据存储运行相同的单元测试:
@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;
}
我的pojos的单元测试很有价值,因为它们最初是使用第三方web API服务的JSON响应填充的(使用Gson)。我发现将Gson解析的测试与objectify数据存储功能的测试分开是很有价值的。后来,我在集成测试过程中对它们进行了完全测试。
我还没有广泛地使用它,所以如果这可能会导致问题,或者以其他方式剥夺了直接使用Ref的优势,我欢迎来自@stickfigure的意见。
发布于 2019-06-13 03:33:53
我写了code to mock Objectify's Key
and Ref
classes here。
要使用以下命令:
Ref<MyEntity> ref = MockObjectify.ref(myEntity);
以下是源代码:
package present.objectify;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.apphosting.api.ApiProxy;
import com.google.common.cache.LoadingCache;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.Ref;
import com.googlecode.objectify.impl.KeyMetadata;
import com.googlecode.objectify.impl.Path;
import com.googlecode.objectify.impl.translate.CreateContext;
import java.util.Collections;
import java.util.Map;
import java.util.function.Supplier;
import present.engine.Caches;
/**
* Creates Objectify mocks.
*
* @author Bob Lee
*/
public class MockObjectify {
/** Creates a reference to the given instance. */
public static <T> Ref<T> ref(T instance) {
return new Ref<T>() {
@Override public T get() {
return instance;
}
@Override public boolean isLoaded() {
return true;
}
@Override public Key<T> key() {
return MockObjectify.key(instance);
}
};
}
/** Creates a key with a mock application ID. */
public static <T> Key<T> key(T instance) {
@SuppressWarnings("unchecked")
KeyMetadata<T> metadata = (KeyMetadata<T>) keyMetadatas.getUnchecked(instance.getClass());
return inMockEnvironment(() -> Key.create(metadata.getRawKey(instance)));
}
/** Creates a key with a mock application ID. */
public static <T> Key<T> key(Class<? extends T> kindClass, long id) {
KeyMetadata<T> metadata = keyMetadata(kindClass);
return inMockEnvironment(() -> Key.create(KeyFactory.createKey(metadata.getKind(), id)));
}
/** Creates a key with a mock application ID. */
public static <T> Key<T> key(Class<? extends T> kindClass, String name) {
KeyMetadata<T> metadata = keyMetadata(kindClass);
return inMockEnvironment(() -> Key.create(KeyFactory.createKey(metadata.getKind(), name)));
}
/** Creates a key with a mock application ID. */
public static <T> Key<T> key(Key<?> parent, Class<? extends T> kindClass, long id) {
KeyMetadata<T> metadata = keyMetadata(kindClass);
return inMockEnvironment(() -> Key.create(KeyFactory.createKey(parent.getRaw(), metadata.getKind(), id)));
}
/** Creates a key with a mock application ID. */
public static <T> Key<T> key(Key<?> parent, Class<? extends T> kindClass, String name) {
KeyMetadata<T> metadata = keyMetadata(kindClass);
return inMockEnvironment(() -> Key.create(KeyFactory.createKey(parent.getRaw(), metadata.getKind(), name)));
}
private static final ObjectifyFactory factory = new ObjectifyFactory();
private static final LoadingCache<Class<?>, KeyMetadata<?>> keyMetadatas = Caches.create(
type -> new KeyMetadata<>(type, new CreateContext(factory), Path.root()));
@SuppressWarnings("unchecked")
private static <T> KeyMetadata<T> keyMetadata(Class<? extends T> clazz) {
return (KeyMetadata<T>) keyMetadatas.getUnchecked(clazz);
}
private static <T> T inMockEnvironment(Supplier<T> supplier) {
ApiProxy.Environment original = ApiProxy.getCurrentEnvironment();
try {
ApiProxy.setEnvironmentForCurrentThread(mockEnvironment);
return supplier.get();
} finally {
ApiProxy.setEnvironmentForCurrentThread(original);
}
}
private static final ApiProxy.Environment mockEnvironment = new ApiProxy.Environment() {
@Override public String getAppId() {
return "mock";
}
@Override public String getModuleId() {
throw new UnsupportedOperationException();
}
@Override public String getVersionId() {
throw new UnsupportedOperationException();
}
@Override public String getEmail() {
throw new UnsupportedOperationException();
}
@Override public boolean isLoggedIn() {
throw new UnsupportedOperationException();
}
@Override public boolean isAdmin() {
throw new UnsupportedOperationException();
}
@Override public String getAuthDomain() {
throw new UnsupportedOperationException();
}
@Override public String getRequestNamespace() {
throw new UnsupportedOperationException();
}
@Override public Map<String, Object> getAttributes() {
return Collections.emptyMap();
}
@Override public long getRemainingMillis() {
throw new UnsupportedOperationException();
}
};
}
https://stackoverflow.com/questions/28559216
复制相似问题