2009年5月3日日曜日

GAE/Jの単体テスト

基本はGoogleCodeで提供されているドキュメント、「Java/How-To/Unit Testing」の通りでおk。

単体テストに必要なモジュール

以下のモジュールがテスト実行環境に必要。

  • appengine-local-runtime
  • appengine-api-stubs.jar

Eclipse Plug-inを導入した場合は、"${ECLIPSE_HOME}/plugins/com.google.appengine.eclipse.sdkbundle_1.2.0.v200904062334/appengine-java-sdk-1.2.0/lib/impl"のようなフォルダに配置されている。バージョン番号を含んだフォルダ名なので、後々変わるかもしれない。mavenのリポジトリでは"http://www.mvnsearch.org/maven2/com/google/appengine/"配下のフォルダにあるのでここから拾えば良い。

テスト実行環境用のAPIプロキシの実装クラスを準備する

package com.shin1ogawa;

import com.google.apphosting.api.ApiProxy;

public class TestEnvironment implements ApiProxy.Environment {
  public String getAppId() { return "Unit Tests"; }
  public String getVersionId() { return "1.0"; }
  public void setDefaultNamespace(String s) {}
  public String getRequestNamespace() { return null; }
  public String getDefaultNamespace() { return null; }
  public String getAuthDomain() { return null; }
  public boolean isLoggedIn() { return true; }
  public String getEmail() { return null; }
  public boolean isAdmin() { return false; }
}

com.google.appengine.api.users.UserServiceFactory#getUserService()からcom.google.appengine.api.users.Userオブジェクトを取得してテストに使用するのであれば、isLoggedIn()でtrueを返し、getAuthDomain()とgetEmail()でnull以外を返してやれば良い。

テストクラスを作成する - その1

まずは@Beforeと@Afterだけ。

package com.shin1ogawa.service;

import static org.junit.Assert.assertEquals;

import java.io.File;
import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.google.appengine.api.datastore.dev.LocalDatastoreService;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.appengine.tools.development.ApiProxyLocalImpl;
import com.google.apphosting.api.ApiProxy;
import com.shin1ogawa.TestEnvironment;
import com.shin1ogawa.entity.Board;

public class BoardServiceTest {
  @Before
  public void setUp() {
    ApiProxy.setEnvironmentForCurrentThread(new TestEnvironment());
    ApiProxy.setDelegate(new ApiProxyLocalImpl(new File("target/testDataStore")) {
    });
    ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();
    proxy.setProperty(LocalDatastoreService.NO_STORAGE_PROPERTY,
        Boolean.TRUE.toString());
  }

  @After
  public void tearDown() {
    ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();
    LocalDatastoreService datastoreService = (LocalDatastoreService) proxy
        .getService("datastore_v3");
    datastoreService.clearProfiles();
    ApiProxy.setDelegate(null);
    ApiProxy.setEnvironmentForCurrentThread(null);
  }
}

"new ApiProxyLocalImpl(new File("target/testDataStore")) {}"としているが、ここで指定したフォルダ配下にローカルストレージ用のファイルと使用したインデックスが配置される。ただし、今回はApiProxyへの設定で"LocalDatastoreService.NO_STORAGE_PROPERTY"を"true"に設定しているので、実際のファイルへの保存は行われない。あらかじめテストデータが格納された状態でのテストを行いたい場合は、この設定を"false"にした状態でデータの書き込みを行い、それをテストケース毎に保存しておくと良い。

テストクラスを作成する - その2

次に実際のテストクラス。今回は"Board"というEntityクラスと、それに関するアクセサを提供する"BoardService"というクラスがある事を想定して、そのServiceクラスのテストケースとして記述している。こんなカンジで、実際のテストメソッド内では特に特殊な事は何も必要無い。

  @Test
  public void create01() {
    User user = UserServiceFactory.getUserService().getCurrentUser();
    BoardService test = new BoardService();
    Board board = test.create(user, "test title1");
    List<Board> list = test.list();
    assertEquals(1, list.size());
    assertEquals("test title1", board.getTitle());
  }

  @Test
  public void delete01() {
    User user = UserServiceFactory.getUserService().getCurrentUser();
    BoardService test = new BoardService();
    Board board1 = test.create(user, "test title1");
    Board board2 = test.create(user, "test title2");
    List<Board> list = test.list();
    assertEquals(2, list.size());
    assertEquals("test title1", board1.getTitle());
    assertEquals("test title2", board2.getTitle());
    test.delete(board1);
    list = test.list();
    assertEquals(1, list.size());
    assertEquals("test title2", list.get(0).getTitle());
  }

注意点

上記の内容については「Googleが提供しているEclipseのPlug-inでプロジェクトを作成し、自分に必要なライブラリをEclipse的にAdd Build pathした環境」で動作するためのもの。MavenとEclipseを組み合わせているとはまる可能性大。自分はそれではまった。MavenからEclipseプロジェクトを作成していると、datanucleus-core内のモジュールがCLASSPATHに重複しているとなんだかエラーが出てしまい、JDOのモデルクラスのエンハンスが正しく行われなかったり、まぁ色々あったりする。

ただし、Mavenからの単体テストの実行もうまくできる方法がわかっているので、それも後で書く。

コメントを投稿