概括
使用 appengine 的 High Replication Datastore 设置单元测试时,我看到了一些奇怪的行为。
我在下面放了一个完整的例子。问题是来自一个测试用例的持久化对象可用于后面的测试用例,但只有在后面的测试用例在事务中检索它时才可用。据我了解,tearDown 方法应该完全清除数据存储。
我认为我的设置有问题,或者我遗漏了一些重要的东西。请帮忙。
具体问题
给定下面的代码,为什么输出如下?(特别是我加粗的最后一行)
2013 年 9 月 17 日 20:41:35 org.datanucleus.PersistenceConfiguration setProperty
信息:属性 datanucleus.appengine.singletonPMFForName 未知 - 将被忽略
2013 年 9 月 17 日 20:41:36 com.google.appengine.datanucleus.MetaDataValidator 验证
信息:为 com.test.Thing 执行特定于 appengine 的元数据验证
2013 年 9 月 17 日 20:41:36 com.google.appengine.datanucleus.MetaDataValidator 验证
信息:已完成对 com.test.Thing 执行特定于 appengine 的元数据验证
2013 年 9 月 17 日 20:41:36 com.google.appengine.api.datastore.dev.LocalDatastoreService 初始化
信息:本地数据存储已初始化:类型:主/从存储:内存中 17-Sep-2013 20:41:36 com.google.appengine.api.datastore.dev.LocalDatastoreService init
信息:本地数据存储已初始化:类型:主/从存储:内存中
[外部交易] 这会正确执行。
[INSIDE TRANSACTION] 这不应该被执行。东西:项目
我的评论
除非我正在做一些非常愚蠢的事情,这并非不可能,否则似乎在第一个测试用例中保留的项目仍然可用于第二个测试用例中的事务。
第二个测试用例尝试在事务之外获取对象ById,并正确抛出异常。
然后它在事务中尝试相同的操作并检索一个对象,我认为该对象只能是早期测试用例中保留的对象。
为什么是这样?我做错了什么?首先十分感谢。
复制问题的代码
我在 Eclipse 中创建了一个新的 Web 应用程序项目,并将此屏幕截图中显示的类和 jar 添加到其中:
PMF 类如下所示:
package com.test;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
public final class PMF {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");
private PMF() {}
public static PMF getInstance() {
if (null == _instance) {
_instance = new PMF();
}
return _instance;
}
public static PersistenceManagerFactory get() {
return pmfInstance;
}
private static PMF _instance;
}
类事物看起来像这样:
package com.test;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
@PersistenceCapable
public class Thing {
public Thing(String item) {
this.item = item;
this.key = makeKey(item);
}
public static Key makeKey(String item) {
return KeyFactory.createKey("Thing", item);
}
public String getId() {
if (null == key) {
return null;
}
return KeyFactory.keyToString(key);
}
public String getItem() {
return item;
}
public String toString() {
return "THING:" + item;
}
@PrimaryKey
@Persistent
private Key key;
@Persistent
private String item;
}
最后,TestDatastore 类如下所示:
package com.test;
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import junit.framework.TestCase;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
public class TestDatastore extends TestCase {
@Override
public void setUp() {
helper.setUp();
}
@Override
public void tearDown() {
helper.tearDown();
}
public void testCreateThing() {
String item = "item";
Thing thing = new Thing(item);
PersistenceManager pm = PMF.get().getPersistenceManager();
Thing persisted = pm.makePersistent(thing);
Thing result = pm.getObjectById(Thing.class, persisted.getId());
assertEquals(item, result.getItem());
}
public void testThingDoesntExist() {
String item = "item";
Thing thing = new Thing(item);
PersistenceManager pm = PMF.get().getPersistenceManager();
Transaction tx = pm.currentTransaction();
try {
Thing testThing = pm.getObjectById(Thing.class, Thing.makeKey(thing.getItem()));
} catch (Exception e) {
System.out.println("[OUTSIDE TRANSACTION] This correctly gets executed.");
}
try {
tx.begin();
Thing testThing = pm.getObjectById(Thing.class, Thing.makeKey(thing.getItem()));
System.out.println("[INSIDE TRANSACTION] This should not be executed. " + testThing);
tx.commit();
} catch (Exception e) {
System.out.println("This doesn't get executed, but it should");
}
}
private final LocalServiceTestHelper helper =
new LocalServiceTestHelper(
new LocalDatastoreServiceTestConfig()
.setDefaultHighRepJobPolicyUnappliedJobPercentage(100));
}