SiNBLOG

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

Slim3 UnitTestでXGTransactionを使う時の課題

Slim3 Source Code Reading #4
に参加してきました。

その時に、話題になったのが、UnitTestでXGTransactionを使う時に、
そのクラス単体でテストすると動作するのに、プロジェクト全体のテストを行う時は、Errorになるということ。

使い方自体は、shin1ogawa殿がBlogに書いて下さったので、これでできる!と思ったのですが、何故かプロジェクト全体でテストを行うと、動かない・・・。

404 shin1のつぶやき ないわー Not Found: Slim3+Cross Group TransactionをUnit Testで使う #gaeja

直接、Slim3 Source Code Reading とは関係なかったのですが、「なぜなのか分からない」ともらしたら、CGTUGマネージャーの横山殿が一緒に調べてくれましたw

横山殿もBlogに書いてくれてます。
T.Yokoyamaのブログ: Slim3 Source Code Reading #4


結論は「Datastore.javaはシングルトンであり、初期化が1度しか走らないため」でした。

slim3.useXGTXを読み取っているのは、AsyncDatastoreDelegate.setUp()です。
このクラスのインスタンスが、Datastoreのクラスメソッドで作成されているため一度しか実行されない。


protected void setUp() {
dsConfig = DatastoreServiceConfig.Builder.withDefaults();
if (deadline != null) {
dsConfig.deadline(deadline);
}
ds = DatastoreServiceFactory.getAsyncDatastoreService(dsConfig);
if (Boolean.valueOf(System.getProperty(USE_XGTX))) {
txOps = TransactionOptions.Builder.withXG(true);
} else {
txOps = TransactionOptions.Builder.withDefaults();
}
}

setUp()で、System.setProperty("slim3.useXGTX", "true");をしているのは、該当のテストクラスだけだったので、他のテストクラスが先に動くと設定が反映されないということですね。


しかし、これをどうするかは悩みものです。
思いつくのは、以下の2つ。

  1. 全てのテストをHRDにする
  2. Slim3のXGTXは利用せずに、LowLevelAPIを利用する。

実際の環境はHRDなので、UnitTestもHRDで実行するのが良いのかもしれません。
ただ、Index更新の遅延によりQueryが取得に失敗するので、結構テストがやりにくくなってしまいます。

公式のテストもHRDでIndex更新の遅延が発生するとErrorになります。
つぶやきの一覧表示 - Slim3 日本語サイト(非公式)


@Test
public void getTweetList() throws Exception {
Tweet tweet = new Tweet();
tweet.setContent("Hello");
Datastore.put(tweet);

//getTweetList()の中でqueryを使っているので、putした直後のデータは取得失敗
List tweetList = service.getTweetList();
assertThat(tweetList.size(), is(1));
assertThat(tweetList.get(0).getContent(), is("Hello"));
}

2つ目のLowLevelAPIを使うというのは、shin1ogawa殿のBlogでも出てました。
XGTransactionを利用していることを、コード上から分かりやすくするというのが目的でしたが、UnitTest時もこっちを使ってる方が楽になります。
そもそも、初期化時に設定されて困るのは"slim3.useXGTX"です。
LowLevelAPIを使えば、これを気にする必要は無いので、解決します。

LowLevelAPIを利用したXGTransactionの例


@Test
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();
}

また、LocalServiceTestHelperを利用して、テストクラスのsetUp()でHRDを設定すると、クラス全体のテストがHRDで実行されるので、HRDの挙動を確認したい場合は、別のテストクラスにした方が良いのかなぁとも思ったり。


どのようにUnitTestを設計していくのがベターなのか。
悩み中です。