実行する
これまでの手順で、メールサービスを利用したプログラムと、そのプログラムに対するテストプログラムが完成しました。プロジェクト名を右クリックして、[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回なら正しい。
このテストプログラムでは、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();
}
- ApiProxyのローカル用インスタンスを取得します。
- 保存用のファイルを作成しないように設定します。
- ローカルのデータストアサービスを取得します(引数は、公式ドキュメントに記載されていたもの)。
- 永続化したデータをクリアします。
私が調べた範囲で、公式のものと思われる情報は見つけられませんでしたが、new LocalDatastoreService().getPackage()の値を設定すると良いみたいです。興味のある方は、公式のMLで質問してみると良いかもしれません。
テストコードの前に、データストアにデータを保存するプログラムを書きますが、詳細については、第5回の解説を参照してください。
ここからのコードは、実際のロジックコードになるので、srcフォルダー側に作成します。
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;
}
}
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;
}
}
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フォルダー側に作成します。
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がコミットされているので、興味のある方は調べてみてはいかがでしょうか?
