今回は読むのを忘れていたModelRefとAppEngineTestCase、AppEngineTesterの
辺りを読みました。
最初にModelRef.javaについて。
読み方は上から順番に読む感じでした。
まず、ModelRef.javaで気にしたのが、getModel()です。
例えば、Slim3本から引用すると、以下のような記述になります。
person.getAddressRef().getMode();このgetModel()が、Memcacheから取得したインスタンスの場合
どうなるかが気になっていたからです。(課金的な面で。)
結果としては、Memcacheに入れてもModelRefに関してはデータストアへのオペレーション回数は減りません。以下のようにトランザクションがない、getを実行するからです。
model = Datastore.getWithoutTx(getModelMeta(), key);
ただし、これは初回だけで、2回目以降の呼び出しではメンバ変数を参照するため、getは実行されません。
また、getAddressRef()は、modelを更新する時にmodelMeta#modelToEntity()メソッド(これは自動生成される)を介してデータストアに保存されますが、データストアにはm.getAddressRef().getKey()で取れるKeyが格納されます。
次に、InverseModelRef.javaについて
これも上から順番に読み進めたのですが、ModelRef.javaと大体同じだったのですが、
getModel()が以下のようになっていました。
model = Datastore.query(getModelMeta()).filter( mappedPropertyName, FilterOperator.EQUAL, key).asSingle();
これは、参照先のModelにプロパティを付加して、それを条件にQueryでデータを取得しています。ただ、1件ならKeyで取れば良いのに…という話になりましたが、よくわからなかったので、ひとまずスルー。
最後に、InverseModelListRef.javaについて
これも上から順番に読み進めました。このクラスは上記2つと違って、内部で、ModelQueryクラスのインスタンスを生成し、modelListを取得します。
複数のデータを取得するので、Filter、Sortも使えます。In-memoryのFilterとSortも使えます。
InverseModelRef、InverseModelListRefの両方とも、指定したプロパティ名のプロパティに格納されているKeyを取得するようになっています。
public ListgetModelList() { Key key = getOwnerKey(); if (key == null) { modelList = new ArrayList (); } else { query.filter(mappedPropertyName, FilterOperator.EQUAL, key); if (!sortsSet) { query.sort(defaultSorts); } modelList = query.asList(); } return modelList; }
最後に、ControllerTestCaseか、AppEngineTestCaseをどちらを先に読むかを迷ったのですが、AppEngineTestCaseから読むことにしました。一般的に先にModelとServiceの作成、テストをするような気がしたので。
AppEngineTestCase.javaは、内部にAppEngineTesterのメンバ変数をもつ、
一般的なJUnitのメソッド(setUp、tearDown)があるだけでした。
したがって、AppEngineTesterを引き続き読み進めました。
AppEngineTesterは、色々ありました。
最初にstaticの初期化がありましたが、AppEngineUtil.isServer()の動きがよくわからなかったので、いろいろテストコードを書いて動かしつつ理解を深めました。
AppEngineUtil.isServer()については、SystemPropertyの"com.google.appengine.runtime.environment"がnullでなければtrueを返します。
これは、開発サーバだと"Development"、本番環境だと"Production"を返します。
(※詳細はドキュメント参照)
しかし、JUnitTestだとnullになります。(たしかに、JUnitTestはサーバではない。)
appengine-web.xmlに記述してみましたが、この値に関しては書き換えることができませんでした。(確認したのはJUnitTestでの実行のみです)
したがって、JUnitTestの時はサーバの環境が取り込まれていないので、初期化時に
必要なライブラリを取り込んでいます。
取り込んでいるのはApiProxyLocalImplとLocalDatastoreService、LocalServerEnvironmentのようです。
setUpでは、ApiProxyのEnvironmentとDelegateを退避しています。これは、恐らく、テストごとに初期化するためだと思われます。
メンバ変数のEnvironmentとDelegateはtearDownで元に戻されます。
tearDownでは、GlobalTransactionのロールバック、アクティブなトランザクションのロールバック、Memcacheのクリア、データストアに書き込みを行ったKeyのエンティティの削除、メールの削除が行われています。
ここで、最初に議論になったのが、「Commitしてしまうと削除されないんじゃないのか?」と
言う話をしました。パッと見た感じだと、deleteKeyを取得している部分がないからです。
が、結論を先に言っておくと、deleteKeyはDatastore#putを呼び出し、実行した時点で
登録されます。
これは、457行目の
ApiProxy.setDelegate(this);
で、自分のインスタンスをApiProxyのDelegateに登録しています。
これを行うことで、APIが実行されるたびに、Delegate<Environment>#makeSyncCall()が呼び出されるようになります。
Delegateはインターフェイスなので実装したクラスである必要がありますが、
AppEngineTesterはDelegateインターフェイスを実装しているので、
515行目のmakeSyncCall、599行目のmakeAsyncCallが呼び出される事になります。
あとは、中身を見ていくと、Service名で条件分岐してそれぞれのAPIに対する処理が
行われています。
Datastoreに関してはmakeSyncCall内でKeyを全て拾っているということになります。
それから、もう一つ重要だったのが、AppEngineTesterのenvironment変数です。
これは、ユーザの情報を持っていて、JUnitTestでUserServiceを利用する時に
重要です。
environment#setEmail()を使ってメールアドレスを設定しておくと、UserService.isUserLoggedIn()がTrueになり、認証済みの状態を作る事ができます。
注意する必要があるのは、emailを空にして、setAdmin(true)にすると、
”ログインしていないが、管理者権限がある”状態が作られてしまいますので、気をつけてください。
これを使うことで、Controllerのテストも可能になります。
同様にFederated Loginの情報も事前にセットしておけば、同じくテストすることが
できるでしょう。(※ただし、自分でセットすることになるので事前にFederated Loginした時の
環境は調査しておく必要があります)
他にもattributeにkeyと値をセットすれば同様に環境を作ることができます。
以下が、環境の動作確認で利用したテストコードです。
public class SampleTest extends AppEngineTestCase { @Test public void サーバチェック() { assertFalse(AppEngineUtil.isServer()); assertNull(SystemProperty.environment.value()); } @Test public void ユーザ認証テスト() { tester.environment.setEmail("test@example.com"); tester.environment.setAdmin(true); UserService user = UserServiceFactory.getUserService(); assertTrue(user.isUserLoggedIn()); assertEquals("test@example.com", user.getCurrentUser().getEmail()); assertTrue(user.isUserAdmin()); } }
他にも色々attributeにセットすると面白い事ができそうですが、
今日はここまでにしておきます。
次回は、2/28(火)です。続きでtesterを読み進める予定です。
https://sites.google.com/site/chugokugtug/sourcereading/slim3/10
ご興味がありましたらどうぞ。
0 件のコメント:
コメントを投稿