2009年9月12日土曜日

Google App EngineでUnit Test

GAE/JでUnit Testするには、いくつか設定が必要。設定方法は、ちゃんとGAE/Jのページにまとめてあります。素晴らしい。
参考:Unit Testing With Local Service Implementations - Google App Engine - Google Code

1.テスト実行環境を作る

まずは、appengine-api-stubs.jarappengine-local-runtime.jarへクラスパスを通す。これらはappengineのsdkをダウンロードしたフォルダのlib/implの下にあります。 分かり易いように、GAE/Jのプロジェクトのwar/WEB-INF/lib下にこれらのjarをコピー。プロジェクトのプロパティーから「Javaのビルドパス」を選び「JARの追加」でクラスパスを通します。
2009.09.14追記:appengine-local-runtime.jarをWEB-INF/lib下に配置しておくと、ローカルでのGAE/Jサーバーの起動に失敗します。jarはlib以外に配置してください。詳しくは「ローカルでGAEサーバーが動かなくなった?!

その後、上記ページを参考にして(というか、そっくりコピペして)、TestEnvironmentクラスを作成。
import com.google.apphosting.api.ApiProxy;

import java.util.HashMap;
import java.util.Map;

class TestEnvironment implements ApiProxy.Environment {
 public String getAppId() {
  return "test";
 }

 public String getVersionId() {
  return "1.0";
 }

 public String getEmail() {
  throw new UnsupportedOperationException();
 }

 public boolean isLoggedIn() {
  throw new UnsupportedOperationException();
 }

 public boolean isAdmin() {
  throw new UnsupportedOperationException();
 }

 public String getAuthDomain() {
  throw new UnsupportedOperationException();
 }

 public String getRequestNamespace() {
  return "";
 }

 public Map<String, Object> getAttributes() {
  return new HashMap<String, Object>();
 }

}

2.基本クラスを作成

1で作成したTestEnvironmentを継承してLocalServiceTestCaseクラスを作る。ローカルサービスを簡単にテストできるようにするための基本クラス。
import java.io.File;

import com.google.appengine.tools.development.ApiProxyLocalImpl;
import com.google.apphosting.api.ApiProxy;

import junit.framework.TestCase;

public class LocalServiceTestCase extends TestCase {

 @Override
 public void setUp() throws Exception {
  super.setUp();
  ApiProxy.setEnvironmentForCurrentThread(new TestEnvironment());
  ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(".")) {
  });
 }

 @Override
 public void tearDown() throws Exception {
  // not strictly necessary to null these out but there's no harm either
  ApiProxy.setDelegate(null);
  ApiProxy.setEnvironmentForCurrentThread(null);
  super.tearDown();
 }

}

3.Datastore Testのための拡張クラス作成

今回、テスト目的がデータストアのチェックだったので、データストアの中身をチェックする時、毎回データをクリーンにしてからテストが実行できるように、LocalServiceTestCaseを継承した以下のようなLocalDatastoreTestCaseクラスも作成。
import com.google.appengine.api.datastore.dev.LocalDatastoreService;
import com.google.appengine.tools.development.ApiProxyLocalImpl;
import com.google.apphosting.api.ApiProxy;

public class LocalDatastoreTestCase extends LocalServiceTestCase {

 @Override
 public void setUp() throws Exception {
  super.setUp();
  ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();
  proxy.setProperty(LocalDatastoreService.NO_STORAGE_PROPERTY,
    Boolean.TRUE.toString());
 }

 @Override
 public void tearDown() throws Exception {
  ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();
  LocalDatastoreService datastoreService = (LocalDatastoreService) proxy
    .getService("datastore_v3");
  datastoreService.clearProfiles();
  super.tearDown();
 }

}

4.TestCaseを書いてみよう

ここまで終わってようやく自分のテストケースを実行できるようになりました。早速テストケースを書いて実行してみます。3で作ったLocalDatastoreTestCaseを継承して作ります。
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Query;

import hoge.Mutter;
import hoge.rpc.MutterUtils;
import hoge.test.LocalDatastoreTestCase;

public class TestMutterUtils extends LocalDatastoreTestCase {

 public void testAdd() {
  MutterUtils mu = new MutterUtils();

  //追加処理

  //追加後データ件数が1件増えていることを確認
  Query query = new Query(Mutter.class.getSimpleName());
  assertEquals(1, DatastoreServiceFactory.getDatastoreService().prepare(
    query).countEntities());
 }

 public void testGetMutterOf() {
  MutterUtils mu = new MutterUtils();

  //追加処理

  //追加後データ件数が1件増えていることを確認
  List<Mutter> mutters = mu.getMutterOf(userName);
  Query query = new Query(Mutter.class.getSimpleName());
  assertEquals(1, DatastoreServiceFactory.getDatastoreService().prepare(
    query).countEntities());

  //追加後のデータを検証
  for (Mutter mutter : mutters) {
   assertEquals("期待した文字列", mutter.getMessage());
  }

 }

}
テストケースを右クリックして「実行」→「JUnitテスト」をクリックしてテストを実行します。

やっぱりローカルでテストできると安心ですねぇ。