無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

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

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

GAEのSDKを1.3.3にバージョンアップしたら、データストアの単体テストコンパイルエラーに! ドキュメントを見ると、どうやらテストケースの書き方が変わったらしい。(1.3.1 - 1.3.2はスルーしていたので、実はずっと前からかもですが)

ということで、SDK1.3.0の頃に書いたテストケース作成手順を1.3.3での手順に更新しておきます。ユーティリティクラスが用意されて、以前より少ないコードで済むようになってますよ。

単体テストとは?

単体テストでは、

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

公式なドキュメントはこちら日本語のドキュメントはまだ更新されていない(2010-05-01 現在)ようなのでご注意。

概要

  1. 必要なモジュールをプロジェクトに追加
  2. テストケース内でLocalServiceTestHelperを作成し、setUp,tearDownでユーティリティのAPIを呼び出す。

1.必要なモジュールをプロジェクトに追加

単体テストの作成に必要な以下のモジュールをプロジェクトに追加します。

  • ${SDK_ROOT}/lib/impl/appengine-api.jar
  • ${SDK_ROOT}/lib/impl/appengine-api-labs.jar
  • ${SDK_ROOT}/lib/impl/appengine-api-stubs.jar
  • ${SDK_ROOT}/lib/testing/appengine-testing.jar

「appengine-api-stubs.jar」「appengine-testing.jar」はEclipseの「Google - Web Application Project」にデフォルトで追加される「App Engine SDK」ライブラリには含まれていないみたいなので、Google App Engine SDKに含まれるものを使用します。
1.3.0の頃に必要だったappengine-local-runtime.jarは不要になったみたいですね。

2.テストケース内でLocalServiceTestHelperを作成し、setUp,tearDownでAPIを呼び出す

後は、テストケース内で「LocalServiceTestHelper」ユーティリティのインスタンスを作成し、setUp,tearDownでユーティリティのAPIを呼び出せば、テストケース内でデータストアにアクセスできるようになります。

/**テストヘルパー*/
private final LocalServiceTestHelper helper =
    new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());

/**
 * 各テスト毎に呼ばれる前準備。
 */
@Before  public void setUp() throws Exception {
    helper.setUp();
}

/**
 * 各テスト毎に呼ばれる後始末。
 */
@After public void tearDown() throws Exception {
    helper.tearDown();
}

以前のようにApiProxyとかは触らなくてOK。だいぶ簡単になってますねー。なお、テストケース内でデータストアに登録したデータは、デフォルトではオンメモリで保持されテストの終了時に破棄されるとのこと。明示的な削除処理は不要です。

テストケースの具体例

以下は、前に作成したCounterServiceのテストケースです。こんな感じでかけますよ、ということで。

import static com.google.inject.matcher.Matchers.annotatedWith;
import static com.google.inject.matcher.Matchers.any;
import static org.junit.Assert.assertEquals;

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

import test.CounterServlet2.CounterService;
import test.CounterServlet2.CounterServiceImpl;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

/**
 * {@link CounterService}のテスト。
 */
public class CounterServiceTest {

    /**テストヘルパー*/
    private final LocalServiceTestHelper helper =
        new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
    
    /**
     * 各テスト毎に呼ばれる前準備。
     */
    @Before  public void setUp() throws Exception {
        helper.setUp();
    }

    /**
     * 各テスト毎に呼ばれる後始末。
     */
    @After public void tearDown() throws Exception {
        helper.tearDown();
    }
    
    /**コンテナ*/
    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() );
    }
}