先ほどのコントローラのテストでは、POST/GETの区別なく、コントローラのメソッド呼び出しを行っていました。しかし、routeファイルでは、POSTでアクセスした場合のみ呼ばれるようにnameResultメソッドが定義されています。
そこで、GETでアクセスした場合と、POSTでアクセスした場合をテストしてみます。testフォルダにRouteTestクラスを作成してください。
import static org.fest.assertions.Assertions.*;
import static play.test.Helpers.*;
import org.junit.Test;
import play.mvc.Result;
public class RouteTest {
	@Test
	public void testNameResultPost(){
		running(fakeApplication(), new Runnable() {
			@Override
			public void run() {
				Result result = routeAndCall(fakeRequest(POST, "/name"));
				assertThat(result).isNotNull();
			}
		});
	}
	@Test
	public void testNameResultGet(){
		running(fakeApplication(), new Runnable() {
			@Override
			public void run() {
				Result result = routeAndCall(fakeRequest(GET, "/name"));
				assertThat(result).isNull();
			}
		});
	}
}
routeファイルのテストを行う際は、callActionメソッドの代わりにrouteAndCallメソッドを利用し、HTTPメソッドとアクセスするURIを指定します。routeAndCallメソッドの返り値は、コントローラのテストの際と同様にResult型で返されます。
また、routeファイルに定義されていないHTTPメソッド/URIを指定した場合は、resultはnullとなります。そのため、GETメソッドで/nameをアクセスしたときはresultがnullかどうかを検証しています。
最後に、実際にブラウザを使ってテストサーバにアクセスし、処理を実行させる形式のテストを書いていきます。ブラウザテストの手順は以下の通りです。
まずは、テストサーバを起動し、トップページにアクセスするだけのコードを書いてみます。以下のクラスを作成してください。
import static org.fest.assertions.Assertions.*;
import static play.test.Helpers.*;
import org.junit.Test;
import org.openqa.selenium.Firefox.FirefoxDriver;
import play.libs.F.Callback;
import play.test.TestBrowser;
public class IntegrationTest {
	@Test
	public void testTop() {
		running(testServer(3333), FirefoxDriver.class, new Callback<TestBrowser>() {
			public void invoke(TestBrowser browser) {
				browser.goTo("http://localhost:3333/top");
			}
		});
	}
}
ブラウザテストでも、コントローラのテスト同様に、runningメソッドを用いてplay runを実行した場合と同じ状況にする必要があります。
ただし、今回はブラウザからのアクセスができるよう、ポートを指定する必要があります。また、どのブラウザでテストするのか、ブラウザのドライバクラスも指定する必要があります。
そのため、runningメソッドの第1引数は、fakeApplicationメソッドの代わりにtestServerメソッドを使います。テストで使うポートはtestServerメソッドの引数に指定します。
第2引数には、テストを行うドライバクラスを指定します。今回はFirefox用のドライバクラスを渡しているので、テストを実行すると自動でFirefoxが立ち上がります。
第3引数には、テストコードを実行するクラスを渡します。コントローラのテストではRunnableインターフェイスを実装したクラスを渡していましたが、今回はブラウザ操作を行うためのドライバを受け取る必要があるため、Callbackインターフェイスを実装するようにします。
テストコードはこのクラスのinvokeメソッドの中に記述し、ブラウザに対する操作は、引数に渡されてくるTestBrowserクラスを利用して行います。
TestBrowserクラスのメソッドは以下のようなものがあります。
| メソッド | 機能 | 
|---|---|
| Fluent goTo(String address) | addressのページを開く。返り値はテストブラウザ自身となる | 
| String title() | 現在のページのタイトルを返す | 
| String url() | 現在のページのURLを返す | 
| FluentList $(String cssSelector) | cssSelectorで示す要素を取得する | 
上記の中で最も重要なものが$メソッドです。$メソッドは、引数にCSSセレクタを指定し、そのCSSセレクタでヒットした要素をFluentList型で返します。
FluentListは、ヒットした要素をFluentWebElement型で保持しおり、firstメソッドやgetメソッドで要素を取得できます。
テストではこのFluentWebElementオブジェクトを使ってブラウザ上のテキストボックスの入力や、ボタンの押下を行います。
FluentWebElementが持つメソッドは以下のようなものがあります。
| メソッド | 機能 | 
|---|---|
| text (String str) | 選択された要素に引数の値を入力する | 
| click() | 選択された要素をクリックする | 
| getTextk() | 選択された要素の文字列を取得する | 
| isEnabledk() | 選択された要素が有効かどうかを返す | 
なお、FluentListについても、上記のメソッドは定義されています。そのため、$メソッドで指定した要素全てについて同一の操作を行って問題なければ、FluentListの中からわざわざFluentWebElementを取得せずに、FluentListに対し上記メソッドを呼び出すことで簡潔にコードが記述できます。
上記メソッドを用いて、トップページにアクセスし、名前を入力して占うボタンをクリックするまでをテストするコードは以下の通りです。
public class IntegrationTest {
    @Test
    public void test() {
	running(testServer(3333),FirefoxDriver.class
		,new Callback<TestBrowser>() {
            public void invoke(TestBrowser browser) {
                browser.goTo("http://localhost:3333/top");
                assertThat(browser.title()).isEqualTo("占い");
                browser.$("input[name=\"name\"]").text("太郎");
                browser.$("input[type=\"submit\"]").click();
                assertThat(browser.title()).isEqualTo("名前占い結果");
                assertThat(browser.$("*").first().getText()).contains("太郎");
            }
        });
    }
}
テストで用いるブラウザのバージョンが新しい場合、テスト実行時にブラウザの操作がうまくいかない場合があります。
この原因として、Play frameworkがあらかじめ用意しているドライバクラスが最新のバージョンに対応していないことが考えられます。
このような場合には、以下のURLから最新のSelenium Serverをダウンロードし、lib以下に配置することで対応が可能です。
上記でテストしたコントローラには、DBアクセスのコードが含まれていませんでした。しかし、DBアクセスがある場合は、テスト用のDBに接続先を変更する必要があるでしょう。その場合は、テストのたびにapplication.confを直接書き換える必要はなく、プログラム上でテストケースごとに接続先を変更可能です。
今までのサンプルでは、ダミーのアプリケーションを作成する際、fakeApplicationメソッドを引数なしで呼び出していました。このメソッドの引数に、Mapを渡すことで、application.confの設定を上書きできます。
@Test
public void testMap() {
	Map<String,String> paramMap = new HashMap<String, String>();
	paramMap.put("db.default.driver", "org.h2.Driver");
	paramMap.put("db.default.url", "jdbc:h2:mem:play");
	running(fakeApplication(paramMap), new Runnable() {
		@Override
		public void run() {
			//DBアクセスを伴うテスト
		}
	});
}
なお、上記のようにDB接続先をインメモリに変更するだけであれば、自分でMapに値を詰めずとも、inMemoryDatabaseメソッドを使うことで簡単に作成が可能です。
@Test
public void testInMemory() {
	running(fakeApplication(inMemoryDatabase()), new Runnable() {
		...
	});
}
ビューとコントローラに対するテストと、実際のブラウザを使ったテストを見てきましたが、Play framework はWebアプリであっても実に簡易なコードでテストが書けるということが理解できたのではないでしょうか。
次回は作成したアプリケーションをHerokuにデプロイし、Web上に公開する方法を紹介します。
 Scala+Play 2.0でWebアプリ開発入門(8):Playのグローバルな設定&spec2でBDDなテスト
Scala+Play 2.0でWebアプリ開発入門(8):Playのグローバルな設定&spec2でBDDなテスト Selenium WebDriverでWebアプリのテストが変わる(前編):iPhone/Android含むブラウザ自動テストの最終兵器Selenium WebDriverとは
Selenium WebDriverでWebアプリのテストが変わる(前編):iPhone/Android含むブラウザ自動テストの最終兵器Selenium WebDriverとは DevOps時代の開発者ための構成管理入門(4):膨大なビルド・テストで泣かないための継続的統合/CI実践ノウハウ
DevOps時代の開発者ための構成管理入門(4):膨大なビルド・テストで泣かないための継続的統合/CI実践ノウハウCopyright © ITmedia, Inc. All Rights Reserved.