Pocket

こんにちは。itouです。

今回は、JUnitでモック(JMockit)を使用し、下記を実現したいと思います。
・検証メソッドの戻り値を指定(複数回呼び出しに対応)
・検証メソッドが呼び出し回数を検証
・検証メソッドの引数を検証(複数回呼び出しに対応)

モック(JMockit)については
下記の記事でも解説しておりますので、ぜひご覧ください。
外部サーバへのHTTPリクエストをJUnit(モック)でテストしよう

検証環境

  • Eclipse 4.4.1
  • Java 1.7
  • JUnit 4.12
  • JMockit 1.17
  • httpclient 4.4.1

Eclipse動作確認手順

Eclipseプロジェクトを添付しておりますので
動作を確認しながらご覧いただければ幸いです。
opentone_labs_201511.zip

  1. 「ファイル」-「インポート」-「既存プロジェクトをワークスペースへ」を選択
  2. 「アーカイブ・ファイルの選択」でopentone_labs_201511.zipを選択しインポート
  3. プロジェクトを右クリックし、「実行」-「JUnit テスト」
  4. JUnitが全て緑(成功)していることを確認

テスト対象機能

下記がテスト対象機能です。
引数のURLに対してGETでHTTPリクエストを行い、HTTPステータスを返却しています。
1回目のリクエストのHTTPステータスが504 Gateway timeoutの場合のみ、2回目をリクエストしています

・HttpServiceImpl.java(テスト対象機能)

public class HttpServiceImpl implements HttpService {
    /**
     * コンストラクタ
     */
    public HttpServiceImpl() {
    }

    @Override
    public int requestUrl(String url) throws IOException {
        int statusCode = request(url);

        // Gateway timeoutの場合は再実行
        if (statusCode == HttpStatus.SC_GATEWAY_TIMEOUT) {
            statusCode = request(url);
        }

        // HTTPステータスを返却
        return statusCode;

    }

    private int request(String url) throws IOException {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        int statusCode;

        try {
            // CloseableHttpClientを生成
            httpClient = HttpClients.createDefault();

            // GETでHTTPリクエストを作成
            HttpGet request = new HttpGet(url);

            // HTTPリクエストを行い、HTTPレスポンスを取得
            response = httpClient.execute(request);

            // HTTPレスポンスよりHTTPステータスを取得
            statusCode = response.getStatusLine().getStatusCode();

        } finally {
            if (httpClient != null) {
                // CloseableHttpClientをclose
                httpClient.close();
            }

            if (response != null) {
                // CloseableHttpResponseをclose
                response.close();
            }
        }
        // HTTPステータスを返却
        return statusCode;
    }
}

テストコード

テスト対象機能においてHTTPリクエストを行っているhttpClient.executeメソッドについて
下記3点の検証をしたいと思います(※HttpClientについてはこちらを参照)

  1. 1回目のリクエストが504 Gateway timeoutとなった場合、httpClient.executeが2回呼ばれていること
  2. 2回ともGETでリクエストされていること
  3. 2回とも引数のURL(★dummy★)が指定されていること

「new NonStrictExpectations() 」の箇所で検証メソッドの戻り値を指定します。
resultに複数回指定することで、1回目はHTTPステータス=504 Gateway timeout、2回目は200 OKが返却・・・のように設定できます。
      
「new Verifications()」の箇所で検証メソッドが呼び出された回数と引数を検証します。
回数は、「times = 2」で検証しています。
引数は、httpClient.executeの引数に指定された内容がactualHttpUriRequestListにリストで格納されるので、それを検証しています。

・UnusedMockedHttpServiceTest.java

    @Test
    public void モックの呼ばれた回数とモックに渡されたパラメータを検証() throws Exception {
        // 1回目はHTTPステータス504 Gateway timeout、2回目は200 OKが返却されるようモックに設定
        new NonStrictExpectations() {
            {
                // モックの対象メソッドを指定
                mockHttpClient.execute((HttpUriRequest) any);

                // CloseableHttpClient#executeの戻り値を生成
                result = new HttpResponseImplDummy(HttpStatus.SC_GATEWAY_TIMEOUT);
                result = new HttpResponseImplDummy(HttpStatus.SC_OK);
            }
        };

        // モックを使用するので、実際には ★dummy★ にリクエストされない
        int actual = httpService.requestUrl("★dummy★");

        // HTTPステータスが200 OKであること
        assertThat(actual, is(HttpStatus.SC_OK));

        // モックが期待通りに呼ばれたか検証
        new Verifications() {
            {
                // 呼ばれた時の引数を取得
                List<HttpUriRequest> actualHttpUriRequestList = new ArrayList<>();
                mockHttpClient.execute(withCapture(actualHttpUriRequestList));

                // 対象メソッドが呼ばれた回数が2回であること
                times = 2;

                // 1回目:GETでリクエストされていること
                assertThat(actualHttpUriRequestList.get(0), instanceOf(HttpGet.class));

                // 1回目:URIが期待通りであること
                assertThat(actualHttpUriRequestList.get(0).getURI().toString(), is("★dummy★"));

                // 2回目:GETでリクエストされていること
                assertThat(actualHttpUriRequestList.get(1), instanceOf(HttpGet.class));

                // 2回目:URIが期待通りであること
                assertThat(actualHttpUriRequestList.get(1).getURI().toString(), is("★dummy★"));

            }
        };
    }

モック(JMockit)活用により
・検証メソッドの戻り値を指定(複数回呼び出しに対応)
・検証メソッドが呼び出し回数を検証
・検証メソッドの引数を検証(複数回呼び出しに対応)
が実現できました。