データストアの単体テストを書く手順
追記:2010-05-01
以下は1.3.0の頃のテスト手順です。最新のSDKではテストケースの書き方が変わっています。1.3.3でのテスト手順はこちらを参照。
いまさらですが、データストアの単体テストを書く手順のメモです。単体テストでは、
- ローカルでGoogle App Engineのサーバーを起動することなしに、
- データストアにアクセスするモジュールのテストを記述できます。
なお、公式なドキュメントはこちらにあるので、あわせて参照ください。
概要
- 必要なモジュールをプロジェクトに追加
- Environmentクラスを作成
- テストケースのsetUpでApiProxyの設定を行う
1.必要なモジュールをプロジェクトに追加
単体テストの作成に必要な以下のモジュールをプロジェクトに追加します。
- appengine-api-stubs.jar
- appengine-local-runtime.jar
Eclipseの「Google - Web Application Project」にデフォルトで追加される「App Engine SDK」ライブラリには含まれていないみたいなので、これを使用している場合は手動で追加する必要があります。モジュールはGoogle App Engine SDKの「/lib/impl」に含まれるものを使用しました。
2.Environmentクラスを作成
次にEnvironmentクラスを用意します。とりあえず、公式なドキュメントのサンプルをコピペして作成。
import java.util.HashMap; import java.util.Map; import com.google.apphosting.api.ApiProxy; /** * テスト環境 */ public class Environment 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 false; } public String getEmail() { return null; } public boolean isAdmin() { return false; } public Map<String, Object> getAttributes() { return new HashMap<String, Object>(); } }
3.テストケースのsetUpでApiProxyの設定を行う
後は、テストケースのsetUpでApiProxyに以下の設定を行えば、テストケース内でデータストアにアクセスできるようになります。
- ApiProxy.setEnvironmentForCurrentThread()で、2で作成したEnvironmentを設定。
- ApiProxyのローカル版実装であるApiProxyLocalImplをApiProxy.setDelegate()で指定。
@Before public void setUp() throws Exception { // ApiProxyの設定 ApiProxy.setEnvironmentForCurrentThread(new Environment()); ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(".")){}); } @After public void tearDown() throws Exception { // ApiProxyの設定を破棄 ApiProxy.setDelegate(null); ApiProxy.setEnvironmentForCurrentThread(null); }
テストケースの具体例
以下は、前に作成したCounterServiceのテストケースです。こんな感じでかけますよ、ということで。
import static com.google.inject.matcher.Matchers.annotatedWith; import static com.google.inject.matcher.Matchers.any; import static org.junit.Assert.assertEquals; import java.io.File; import org.junit.After; import org.junit.Before; import org.junit.Test; import test.CounterServlet2.CounterService; import test.CounterServlet2.CounterServiceImpl; import com.google.appengine.api.datastore.dev.LocalDatastoreService; import com.google.appengine.tools.development.ApiProxyLocalImpl; import com.google.apphosting.api.ApiProxy; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; /** * {@link CounterService}のテスト。 */ public class CounterServiceTest { /** * 各テスト毎に呼ばれる前準備。 */ @Before public void setUp() throws Exception { // ApiProxyの設定 ApiProxy.setEnvironmentForCurrentThread(new Environment()); ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(".")){}); } /** * 各テスト毎に呼ばれる後始末。 */ @After public void tearDown() throws Exception { // テストで登録したデータを削除 ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate(); LocalDatastoreService datastoreService = (LocalDatastoreService) proxy.getService("datastore_v3"); datastoreService.clearProfiles(); // ApiProxyの設定を破棄 ApiProxy.setDelegate(null); ApiProxy.setEnvironmentForCurrentThread(null); } /**コンテナ*/ static final Injector HOME = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind( CounterService.class ).to( CounterServiceImpl.class ); bindInterceptor( any(), annotatedWith( Tx.Persistence.class ), new Tx.TxInterceptor()); } }); /** * カウントアップのテスト。 */ @Test public void countup() { CounterService service = HOME.getInstance(CounterService.class); assertEquals( 0, service.count() ); assertEquals( 1, service.count() ); assertEquals( 2, service.count() ); assertEquals( 3, service.count() ); } }