単体テストを行うと、作成したコードの品質を検証するだけでなく、コードの作成を進めながら開発プロセスの改善を行えます。アプリケーションの開発が終わった後にテストを作成するのではなく、開発プロセスと並行して単体テストを作成することを検討してください。これにより、保守が簡単で再利用できる単体コードを作成できます。また、コードのテストをすばやく、徹底的に行うことができます。
ローカルで単体テストを行う場合、ユーザーの開発環境内ですべてのテストを行います。リモートのコンポーネントは使用しません。App Engine のテスト ユーティリティを使用すると、データストアや他の App Engine サービスをローカルに実装できます。コードを App Engine にデプロイする必要はありません。サービススタブにより、これらのサービスをローカルで利用できるようにします。
サービススタブはサービスの動作をシミュレートします。たとえば、データストアと Memcache のテストを作成するで説明するデータストアのサービススタブでは、実際のデータストアにリクエストを送信することなく、データストアのコードをテストできます。データストアの単体テストで生成されるエンティティは、データストアではなくメモリ上に格納され、テストの実行後に削除されます。実際のデータストアに依存することはないため、簡単なテストをすばやく行うことができます。
このドキュメントでは、テスト用のフレームワークについて簡単に説明し、ローカルの App Engine サービスで行う単体テストの作成方法を説明します。
テスト用フレームワークを設定する
SDK のテスト ユーティリティは特定のフレームワークに関連付けられていませんが、この例では JUnit を使用して説明を進めていきます。テストを作成する前に、適切な JUnit 4 JAR をテストのクラスパスに追加してください。追加したら、簡単な JUnit テストを作成してみましょう。
Eclipse を実行している場合には、テストを行うソースファイルを選択します。[Run] メニュー > [Run As] > [JUnit Test] の順に選択します。テストの結果はコンソール ウィンドウに表示されます。
Java 8 テスト ユーティリティについて
MyFirstTest
では、非常に簡単なテストを行います。このテストは、App Engine API やローカルのサービス実装に依存しないため、特に操作を行う必要はありません。ただし、テストやテストで使用するコードにこのような依存関係がある場合には、次の JAR ファイルをテストのクラスパスに追加してください。
${SDK_ROOT}/lib/impl/appengine-api.jar
${SDK_ROOT}/lib/impl/appengine-api-stubs.jar
${SDK_ROOT}/lib/appengine-tools-api.jar
これらの JAR により、ランタイム API やこれらの API のローカル実装をテストで使用できるようになります。
App Engine サービスは、さまざまな実行環境を想定しています。これらの設定を行うには、相当な量のボイラープレート コードが必要になります。自身で設定するのではなく、com.google.appengine.tools.development.testing
パッケージ内のユーティリティを使用できます。このパッケージを利用するには、次の JAR ファイルをテストのクラスパスに追加します。
${SDK_ROOT}/lib/testing/appengine-testing.jar
com.google.appengine.tools.development.testing
パッケージの javadoc を見てみましょう。このパッケージで最も重要なクラスは LocalServiceTestHelper です。このクラスは、必要な環境の準備をすべて行い、テストで必要になるローカル サービスの構成を行います。
特定のローカル サービスを利用するテストを作成するには:
LocalServiceTestHelper
のインスタンスを、その特定のローカル サービスのLocalServiceTestConfig
実装で作成します。LocalServiceTestHelper
インスタンスで、テストの実行前にsetUp()
を呼び出し、テストの実行後にtearDown()
を呼び出します。
データストアと Memcache のテストを作成する
次の例では、データストア サービスを使用しているかどうかテストします。
この例では、実行環境ですべてのローカル サービスに共通する部分の設定と解放を LocalServiceTestHelper
が行い、ローカルのデータストア サービスに固有の部分の設定と解放を LocalDatastoreServiceTestConfig
が行っています。javadoc の説明のとおり、この処理では、(一定の間隔でディスクにフラッシュするのではなく)すべてのデータをメモリ上に格納し、テストが終了するたびにメモリから消去するようにローカルのデータストア サービスを構成しています。これは、データストア テストのデフォルトの動作ですが、必要に応じて変更できます。
データストアではなく memcache にアクセスするように変更する
テストでローカルの memcache サービスにアクセスするように設定するには、上記のコードを少し変更します。
データストア関連のクラスではなく、memcache 関連のクラスをインポートします。その場合でも、LocalServiceTestHelper
をインポートする必要があります。
作成するクラスの名前を変更し、LocalServiceTestHelper
のインスタンスを変更して、Memcache に固有になるようにします。
最後に、テストの実行方法を変更します。
データストアの例の場合と同様に、LocalServiceTestHelper
とサービス固有の LocalServiceTestConfig
(この場合は LocalMemcacheServiceTestConfig
)によって実行環境が管理されます。
Cloud Datastore のテストを作成する
アプリで Cloud Datastore を使用する場合は、結果整合性に即してアプリケーションの動作を検証するテストを作成することをおすすめします。これは LocalDatastoreServiceTestConfig
のオプションにより簡単になります。
未適用のジョブの割合を 100 に設定すると、ローカルのデータストアに対し、最大限の結果整合性で動作するよう指示することになります。最大限の結果整合性とは、書き込みが commit されても常に適用に失敗することを意味します。そのため、グローバル(祖先でない)クエリには一貫して、変更が反映されません。これはもちろん、本番環境での実行時にアプリケーションに反映される結果整合性の程度を表すものではありませんが、テストの目的では、ローカル データストアを毎回このように動作するよう構成できれば非常に便利です。
適用しないトランザクションについてより詳細に制御する場合、独自の HighRepJobPolicy
を登録できます。
テスト用 API は、アプリケーションが結果整合性に即して適切に動作することを検証するために有用ですが、ローカルの高レプリケーション読み取り整合性モデルは、本番環境の高レプリケーション読み取り整合性モデルの近似モデルであり、厳密なレプリカではないことに注意してください。ローカル環境では、適用されない書き込みがあるエンティティ グループに属する Entity
の get()
を実行すると常に、その適用されない書き込みの結果が、以降のグローバル クエリで認識されます。本番環境の場合、これは該当しません。
タスクキューのテストを作成する
ローカルのタスクキューを使用するテストは少し複雑になります。データストアや memcache の場合と異なり、タスクキューの API にはサービスの状態を調べる方法がありません。予想したパラメータでタスクのスケジュールが設定されているかどうか確認するには、ローカルのタスクキューにアクセスする必要があります。これを行うためには、com.google.appengine.api.taskqueue.dev.LocalTaskQueue
が必要です。
ここでは、タスクが予期されたとおりにスケジュールされたかを確認するため、ローカル サービス インスタンスのハンドルを LocalTaskqueueTestConfig
で呼び出し、ローカル サービスを直接調べています。すべての LocalServiceTestConfig
実装で類似したメソッドが公開されています。ここでは必ずしも必要ではありませんが、いずれ必要になります。
queue.xml
構成ファイルの設定
タスクキューのテスト ライブラリを使用すると、LocalTaskQueueTestConfig.setQueueXmlPath
メソッドで複数の queue.xml
構成を per-LocalServiceTestHelper に設定できます。現在、ローカルの開発用サーバーでは、キューのレート制限が無視されます。ローカルでは複数のタスクを同時に実行することはできません。
たとえば、App Engine アプリケーションがアップロードして使用する queue.xml
ファイルのテストをプロジェクトで行う必要があるとします。queue.xml
ファイルが標準的な場所にあると想定すると、上記のサンプルコードを次のように変更して、src/main/webapp/WEB-INF/queue.xml
ファイルで指定されているキューへのテストアクセス権を付与できます。
プロジェクトのファイル構造に適合するように、queue.xml
ファイルへのパスを変更します。
QueueFactory.getQueue
メソッドを使用して、キュー名でアクセスします。
遅延タスクのテストを作成する
アプリケーション コードで遅延タスクを使用している場合、Java テスト ユーティリティを使用すると、これらのタスクの結果を検証する統合テストを簡単に作成できます。
最初のローカル タスクキューの例と同様に LocalTaskqueueTestConfig
を使用していますが、今回は別の引数を追加してキューを初期化しています。これにより、タスクのスケジュールだけでなく、実行状態も簡単に検証できます。ここでは、setDisableAutoTaskExecution(false)
を呼び出し、ローカル タスクキューにタスクの自動実行を指示しています。setCallbackClass(LocalTaskQueueTestConfig.DeferredTaskCallback.class)
を呼び出し、ローカル タスクキューにコールバックの使用を指示しています。これにより、遅延タスクの実行方法を確認できます。最後に、setTaskExecutionLatch(latch)
を呼び出し、各タスクの実行後にラッチをデクリメントするようにローカル タスクキューに指示しています。これにより、遅延タスクをキューに入れて待機し、予期したとおり実行されるかどうか検証するテストを作成できます。
ローカル サービスの機能テストを作成する
機能テストでは、データストア、Blobstore、memcache などのサービスのステータスを変更し、そのサービスでアプリケーションを実行して、異なる条件下でアプリケーションが予期したとおり実行されるかどうかを検証します。機能のステータスを変更するには、LocalCapabilitiesServiceTestConfig クラスを使用します。
次のコード スニペットでは、データストア サービスのステータスを無効にして、データストア サービスのテストを行っています(必要に応じて、データストア以外のサービスも利用できます)。
サンプルテストでは、まず、データストアに初期化された Capability
オブジェクトを作成し、CapabilityStatus
オブジェクトを作成して DISABLED に設定します。次に、作成した Capability
オブジェクトと CapabilityStatus
オブジェクトを使用して、機能とステータスが設定された LocalCapabilitiesServiceTestConfig
を作成します。
さらに、LocalCapabilitiesServiceTestConfig
オブジェクトを使用して、LocalServiceHelper
を作成します。これでテストの設定が完了です。作成した DatastoreService
にクエリを送信し、予期した結果(この場合は CapabilityDisabledException
)が生成されるかどうか確認します。
他のサービスのテストを作成する
テスト ユーティリティは、Blobstore や他の App Engine サービスでも使用できます。テストにローカル実装を使用するすべてのサービスのリストについては、LocalServiceTestConfig
のドキュメントをご覧ください。
認証テストを作成する
この例では、テストを作成します。UserService を使用するロジックを検証し、ユーザーがログオンしているかどうかとユーザーに管理者権限があるかどうかを確認します。基本ロールが閲覧者、編集者、オーナーのユーザーや App Engine アプリ管理者の事前定義ロールには管理者権限が設定されています。
この例では、LocalUserServiceTestConfig
で LocalServiceTestHelper
を構成しているため、テストに UserService
を使用できますが、認証関連の環境データを LocalServiceTestHelper
自体に構成することも行います。
この例では、LocalUserServiceTestConfig
で LocalServiceTestHelper
を構成しているため、OAuthService
を使用できます。