読者です 読者をやめる 読者になる 読者になる
無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

データストアの単体テストを書く手順

Java Google App Engine

追記:2010-05-01
以下は1.3.0の頃のテスト手順です。最新のSDKではテストケースの書き方が変わっています。1.3.3でのテスト手順はこちらを参照。

いまさらですが、データストアの単体テストを書く手順のメモです。単体テストでは、

  • ローカルでGoogle App Engineのサーバーを起動することなしに、
  • データストアにアクセスするモジュールのテストを記述できます。

なお、公式なドキュメントはこちらにあるので、あわせて参照ください。

概要

  1. 必要なモジュールをプロジェクトに追加
  2. Environmentクラスを作成
  3. テストケースの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() );
    }
}