SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Google App Engine for Javaを使ってみよう!

Google App Engine for Javaを使ってみよう!
(8)Unitテスト

Google App Engine for Javaを使ってみよう! (8)

実行する

 これまでの手順で、メールサービスを利用したプログラムと、そのプログラムに対するテストプログラムが完成しました。プロジェクト名を右クリックして、[Run As]-[JUnit Test]を選択してください。

 以下のように緑のゲージが表示されていればテストは成功です。

 次は、テストを失敗させてみたいと思います。まずは、正常に動作しているテストプログラム(MailSenderTestクラスに定義されているtestSendMailメソッド)を確認してみましょう。

public void testSendMail() {
    ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();
    LocalMailService mailService = (LocalMailService) proxy.getService("mail");
    mailService.clearSentMessages();  // (1)

    MailSender.sendMail("test@example.com", "test-user");  // (2)

    assertEquals(1, mailService.getSentMessages().size());  // (3)
}
  1. メールを送ったという記録をクリアします。
  2. メールを送信するプログラムにアクセスします。
  3. メールを送った回数が1回なら正しい。

 このテストプログラムでは、MailSender.sendMail()というプログラムが正しくメールを送れていれば、メールの送信回数が1回になっているハズというテストを実行していますので、MailSenderクラスの29行目にあるTransport.send(message)をコメントアウトしてメールを送らないようにプログラムを書き換えるとテストが失敗するハズです。

 プログラムを書き換えたら、プロジェクト名を右クリックして、[Run As]-[JUnit Test]を選択してください。ゲージが以下のように赤くなれば、正しくエラーになったことが分かります。

 データストアをテストする

 続いて、データストアのテストを行ってみたいと思います。まずは、データストアのテストプログラムで重要な箇所を見ていきましょう。

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

    ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();  // (1)
    proxy.setProperty(LocalDatastoreService.NO_STORAGE_PROPERTY,
            Boolean.TRUE.toString());  // (2)
}

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

    ApiProxy.setDelegate(null);
    ApiProxy.setEnvironmentForCurrentThread(null);
    super.tearDown();
}
  1. ApiProxyのローカル用インスタンスを取得します。
  2. 保存用のファイルを作成しないように設定します。
  3. ローカルのデータストアサービスを取得します(引数は、公式ドキュメントに記載されていたもの)。
  4. 永続化したデータをクリアします。
ApiProxyLocalImpl.getService()の引数について

 私が調べた範囲で、公式のものと思われる情報は見つけられませんでしたが、new LocalDatastoreService().getPackage()の値を設定すると良いみたいです。興味のある方は、公式のMLで質問してみると良いかもしれません。

 テストコードの前に、データストアにデータを保存するプログラムを書きますが、詳細については、第5回の解説を参照してください。

 ここからのコードは、実際のロジックコードになるので、srcフォルダー側に作成します。

com.daisukeyamashita.test.unit.PMF クラス
package com.daisukeyamashita.test.unit;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public class PMF {

    private static final PersistenceManagerFactory pmfInstance = JDOHelper
            .getPersistenceManagerFactory("transactions-optional");

    private PMF() {
    }

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }
}
com.daisukeyamashita.test.unit.StoreData クラス
package com.daisukeyamashita.test.unit;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class StoreData {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    @Persistent
    private String name;

    public StoreData(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

 

com.daisukeyamashita.test.unit.StoreService クラス
package com.daisukeyamashita.test.unit;

import javax.jdo.PersistenceManager;

public class StoreService {

    public void store(String name) {
        StoreData storeData = new StoreData(name);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(storeData);
        } finally {
            pm.close();
        }
    }
}

 以下のcom.daisukeyamashita.test.unit.StoreServiceTest クラスは、テストコードなので、src_testフォルダー側に作成します。

com.daisukeyamashita.test.unit.StoreServiceTest クラス
package com.daisukeyamashita.test.unit;

import java.io.File;

import junit.framework.TestCase;

import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.dev.LocalDatastoreService;
import com.google.appengine.tools.development.ApiProxyLocalImpl;
import com.google.apphosting.api.ApiProxy;

public class StoreServiceTest extends TestCase {

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

        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();

        ApiProxy.setDelegate(null);
        ApiProxy.setEnvironmentForCurrentThread(null);
        super.tearDown();
    }

    public void testStoreSuccess() {
        StoreService service = new StoreService();
        service.store("蕎麦");

        Query query = new Query(StoreData.class.getSimpleName());
        assertEquals(1, DatastoreServiceFactory.getDatastoreService().prepare(
                query).countEntities());

        service.store("うどん");
        assertEquals(2, DatastoreServiceFactory.getDatastoreService().prepare(
                query).countEntities());
    }
}

  DatastoreServiceFactory.getDatastoreService().prepare(query).countEntities()でデータストアに保存されているデータの数を取得できます。データの数だけでなく、中身も確認したい場合にはcountEntities()の代わりにasIterable()asIterator()を利用することでデータをリスト形式で取得することも可能です。

 テストを実行するには、先ほどと同じようにPackage ExplorerからStoreServiceTest.javaを右クリックして、[Run As]-[JUnit Test]を選択して下さい。

データストアのテストでエラーが出た場合

 最初の[必要な環境]の項目で書いたように、データストアのテストを行うにはGoogle Plugin for Eclipseのバージョンが、1.2.2以降になっている必要があります。

 

 プラグインを更新した場合でも、古いプラグインは削除されませんので、プロジェクトの設定が古いSDKを参照している場合があります。プロジェクトのプロパティの[Google]-[App Engine]で選択されているSDKが[App Engine - 1.2.2]か[App Engine - 1.2.5]になっていることを確認して下さい。

 

まとめ

 今回はGoogle App Engineのプログラムに対するUnitテストを勉強しましたが、いかがでしたでしょうか? 今回の記事で、Google App Engine上にプログラムをアップロードすることなく、プログラムをテストできることが分かっていただけたかと思います。

 Unitテストに関する項目は非常に重要なハズなのですが、公式サイトにも情報がほとんど掲載されておらず、難しいことをするには手探りで調べる必要があります。

 Google App Engineのライブラリの一部はソースコードが公開されています。そちらのリポジトリにcom.google.appengine.tools.development.ApiProxyLocalなどのJavaDocがコミットされているので、興味のある方は調べてみてはいかがでしょうか?

参考資料

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
Google App Engine for Javaを使ってみよう!連載記事一覧

もっと読む

この記事の著者

山下 大介(ヤマシタ ダイスケ)

  京都大学を中心とした、産官学共同プロジェクトのSOBAプロジェクトに参加後、同プロジェクトで開発したP2P配信によるVoIP技術を2005年に商業化。オープンソース、VoIP、P2P、クラウドコンピューティングに精通。趣味はGoogleの追っかけ。現在は、株式会社SOBAプロジェクト取締役。ブログ:『~ ミネルヴァの梟は黄昏とともに飛び始める ~』所属:株式会社SOBAプロジェクトGoogle Developer Day 2008サポーター, 2009サポーターGoogle App Engine API Expert

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/3919 2009/09/21 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー