Goalist Developers Blog

Play Frameworkでテストを書く

チナパです! 最近新しいことを学んだので、書いてみようかなと思いました。

なぜテストを書く?

f:id:c-pattamada:20181129134919p:plain
Why do we need tests...?

複雑なプログラムを作るほど、ディバッグ、テスティングも大変になるのが一般的に知られてます。今日作ったソフトを6ヶ月間後編集しようとしますと、なかなか何が何に影響されるのを覚えるのも大変だと思います。

この二つの問題を対応するのに、テスティングをある程度まで(頑張ったら、完全に?)自動化できます。今日はサーバー側の話にします。

ご存知かもしれませんが、ゴーリストではPlay Framworkをよく使ってますので、Playの環境でやってみました。便利なHelperクラスもいくつかありますので、割と簡単にできるかと思います。

なので、Play Frameworkの基本知識が持ってる方が理解するのに楽だと思いますが、少しだけその辺も説明しますので、ご安心ください。

手順

まずは、PlayのProject Directoryを見てみましょう。(私はIntellij IDEAを使ってますが、sbtターミナルが使える状態にできますと特に環境が影響しないと思います)

f:id:c-pattamada:20181129130951p:plain
Play project directory

こちら、testというdirectoryが見えますが、存在してない場合は作成してください。その中にcontrollersのパッケージを作成しましょう。(ちなみに、sbtのスタータープロジェクトを使用する場合に、ここまですでにできているかと思います。)

コード説明

public class HomeControllerTest extends WithApplication {

    @Override
    protected Application provideApplication() {
        return new GuiceApplicationBuilder().build();
    }
}

このようなクラスを作成しましょう。HomeControllerTestではなくても、クラス名はなんでもOKです。

provideApplication()はこのクラスにPlayのApplicationが使えるようになるためにあります。 なぜ必要なのか?

オッケー、このあたりで、一体何を使用としているのかを説明しましょう。

多動でwebサーバーのテストを行う時に、どうしますか?例えば、

http://localhost/my/test/route

がちゃんと動いているかを確認したい場合は、ブラウザーや、Postmanなどのツールを使って、リクエストを送って結果を確認しているかもしれません。

その行動を真似するコードを書きます。簡単に、行動がコードにしましょう。ね。

いかのメソッドを作成しましょう。

    @Test // 1
    public void testMyTestRoute() {
        Http.RequestBuilder request = new Http.RequestBuilder()
                .method(GET)
                .uri("/my/test/route"); // 2

        Result result = route(app, request); // 3
        assertEquals(OK, result.status()); // 4
    }

1) @Testがテストを実装する時に、何メソッドを確認すれば良いのかを知らせるannotationです。

2) HttpRequestBuilderを使って、appの/my/test/routeにGETリクエストを投げてます。(ちなみに、GETがヘルパーコンスタントです)

3) サーバーのレスポンスをもらいます。これはPlayのルーターを使って、どこかのコントローラのメソッドをテストしています。

4) Httpレスポンスがstatusコードを持つ、ここで、そのstatusが200なのかを確認しています。assertEqualsはJUnitのテスティングフレームワークのメソッドです。二つのパラメーターが同じではない場合に、テストが失敗します。

上記のメソッドでこのようなことをテストできます。宛先のuriがちゃんと壊れずにうまく行きますか?(バグが出てる場合に、result.status()はおそらく500<=なので、ここでテストが失敗します。)

複数な@Testメソッドをもちろん作成した方が良いです。 例として、

1)ユーザー登録成功のテスト(ちゃんと登録できる確認)

2)ユーザーが登録で誤ったリクエスト(例:すでに登録したユーザーが弾かれる確認)

3)ログイン

…などなど。つまり一つのuriが複数のメソッドでテストされてるのもおかしくありません。自分はだいたい成功のテストとエラーのテストを分ける趣味です。

実装

さて、テストが作成できましたら、どうやって実装しますか?

IDEAでは、画面の下にあるsbt shellが使えます。別な環境では、project ディレクトリをターミナルで開いて、 sbtを打てば同じ環境に入れます。

起動が終わりましたら、

test

を打ってエンター押せば@Testのメソッドが実装されます。

はい。簡単です。(大変な量のログが出た後で、というアウトプットが見れます)

f:id:c-pattamada:20181129131042p:plain
成功した時

うまくいってない場合は以下のようにエラーログが出て、何がどこに壊れてるのかの当てが掴みやすくなると思います。

f:id:c-pattamada:20181129131126p:plain
あらら

結論

今日作成しているプログラムが来年別な人にメンテされることもあるでしょう。自分がまだ対応している場合にも、なかなか覚えてないこともあるでしょう。

そこで、知らず知らずにバグを起こしたら、testの段階でわかりやすくなるし、すぐに原因の尻尾も掴むこともできますので、ぜひテストを利用してコードを書きましょう!