SiNBLOG

140文字に入らないことを、極稀に書くBlog

XG Transaction がUnitTestで動かない。

Slim3のUnitTestでXGTransactionを動作させることができなくてハマっている。

とりあえず、現状は以下の通り。

■環境
GAE 1.6.0
Slim3 1.0.15
Eclipse 3.5


まずは、実行の構成をHRDに設定
http://code.google.com/intl/en/appengine/docs/java/tools/devserver.html
割りと最近作成したプロジェクトだったので、最初からHRDだったので、実際は確認しただけ。


次にappengine-web.xmlに"slim3.useXGTX"を追加した。


sin4sandbox
1

true














true
true

これでLocalでWebアプリケーションを起動した場合は、HRDで動作するようになった。
しかし、UnitTestではM/Sで起動してしまい、XG Transactionが動かない。


次に、LocalServiceTestHelperが必要なのかと思い、設定してみたが特に変わらなかった。
http://code.google.com/intl/en/appengine/docs/java/tools/localunittesting.html#Writing_HRD_Datastore_Tests
また、${SDK_ROOT}/lib/testing/appengine-testing.jarをテスト用クラスパスに追加する
という文章の意味がよく分からなかったので、とりあえずslim3-gen.jarと同じlibの中にいれた。
ktrwjrが動かなくなってしまいそうだが、WEB-INF/libに入れるとLocalServerが起動しなくなった。
同じように、appengine-api-stubs.jarも配置した。
他に良い方法があれば、教えていただけるとありがたいです・・・。


public class TwitterServiceTest extends AppEngineTestCase {

private TwitterService service = new TwitterService();

private final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
.setDefaultHighRepJobPolicyUnappliedJobPercentage(100));

/* (非 Javadoc)
* @see org.slim3.tester.AppEngineTestCase#setUp()
*/
@Override
public void setUp() throws Exception {
helper.setUp();
super.setUp();
}

/* (非 Javadoc)
* @see org.slim3.tester.AppEngineTestCase#tearDown()
*/
@Override
public void tearDown() throws Exception {
super.tearDown();
helper.tearDown();
}

@Test
public void test() throws Exception {
assertThat(service, is(notNullValue()));
}
}

実際にテストしようとしているのは、以下のソース。
ただ単にTransactionの中で、複数のEntityGroupをputしているだけ。


@Test
public void testXGTransaction() throws Exception {
Transaction tx = Datastore.beginTransaction();
Datastore.put(new Entity("hoge"));
Datastore.put(new Entity("fuga"));
Datastore.put(new Entity("moga"));
tx.commit();
}

これが本番環境やLocalServerだと動くのだが、UnitTestでは動作しない。
まだ、他に設定しなければならないことがあるのだろうか?


2011/01/13 追記
shin1ogawa 殿がご教授下さいました!
UnitTest時には、appengine-web.xmlが読み込まれないので、slim3.useXGTXをsetUp()時に
指定してやる必要があるそうです。


public class TwitterServiceTest extends AppEngineTestCase {

private TwitterService service = new TwitterService();

private final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
.setDefaultHighRepJobPolicyUnappliedJobPercentage(100));

/* (非 Javadoc)
* @see org.slim3.tester.AppEngineTestCase#setUp()
*/
@Override
public void setUp() throws Exception {
helper.setUp();
super.setUp();
System.setProperty("slim3.useXGTX", "true");
}

/* (非 Javadoc)
* @see org.slim3.tester.AppEngineTestCase#tearDown()
*/
@Override
public void tearDown() throws Exception {
super.tearDown();
helper.tearDown();
}

@Test
public void test() throws Exception {
assertThat(service, is(notNullValue()));
}
}

また、shin1ogawa殿のブログにも、詳細が載っています。
404 shin1のつぶやき ないわー Not Found: Slim3+Cross Group TransactionをUnit Testで使う #gaeja

shin1ogawa 殿はSlim3.useXGTXは使わずに、LowLevelAPIを直接使った方が
分かりやすくて良いのではないか?と仰ってますね。
確かにGlobalTransactionの時はクラスやメソッドが異なっていましたが、
XGTransactionでは、単一のTransactionと同一です。
コストや性能は単一のTransactionと同一である。と公式にはありますが、
複数のEntityGroupを扱っているという認識はコードから読み取れた方が良いかもですね。
http://code.google.com/intl/en/appengine/docs/go/datastore/overview.html#Cross_Group_Transactions

Slim3.useXGTXは使わずに、LowLevelAPIを直接使った場合は、こんな感じかな?


public void testLowLevelAPIXGTransaction() throws Exception {
DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
TransactionOptions txOps = TransactionOptions.Builder.withXG(true);

Transaction tx = datastoreService.beginTransaction(txOps);
Datastore.put(new Entity("hoge"));
Datastore.put(new Entity("fuga"));
Datastore.put(new Entity("moga"));
tx.commit();
}