JUnitでMockWebServerを使ってリクエスト送受信ロジックのテスト


Javaアプリケーションに外部APIを使った機能を実装する機会があったのでその時の話を。

やりたいこと

リクエスト送受信処理を新たに実装した場合、固定レスポンスで期待した動作をするのか確かめたい気持ちになります。
そこで、MockWebServerというライブラリを使って固定レスポンスを返すことにしました。これにより、リクエスト送受信の部分のロジック検証を簡易化できます。

開発環境

今回試すJava環境は下記の通りです。

$ java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)

ビルドツールはmaven(3.6.0)を使っている想定です。

利用方法

pom.xmlに必要なライブラリを追記します。

<dependency>
  <groupId>com.squareup.okhttp</groupId>
  <artifactId>mockwebserver</artifactId>
  <version>2.7.5</version>
  <scope>test</scope>
</dependency>

実装するにはMockWebServerとMockResponseをimportする必要があります。

MockWebServerがリクエストを受けてレスポンスを返す仕組みを提供するクラスで、MockResponseは専用のレスポンス内容を定義するために使うクラスです。

最低限MockWebServerを動かすためには下記のような実装になります。

import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;

public class QiitaTest {

    private MockWebServer mockWebServer;

    @Before
    public void setup() throws IOException {
        this.mockWebServer = new MockWebServer();
        this.mockWebServer.start();
    }

    @Test
    public void qiitaTest() {
        String json = "{\"message\": \"qiita用テスト\"}";
        MockResponse mockResponse = new MockResponse();
        mockResponse.setBody(json);
        mockResponse.setResponseCode(200);
        this.mockWebServer.enqueue(mockResponse);

        String mockServerUrl = mockWebServer.url("/").toString();
        // mockServerUrl = http://localhost:59690/

        // リクエスト送信処理を書いてレスポンスを受け取ってテスト内容を書く
    }
}

ダミーサーバを立ち上げるためにはMockWebServerのインスタンスを生成し、startメソッドを呼び出します。
ここでは使っていないですが、コンストラクタの引数にポートを指定することが可能です。
mockWebServer.start() は IOException が throws で指定されているため、前処理として定義したsetup()も同様にしています。

これにより、Jacksonなどを使ってレスポンス内容をオブジェクトにマッピングする処理を固定レスポンスで検証できたりします。
※具体的にリクエストを送信する処理は割愛します

上記の様に正常系処理も試せますが、 setBodyDelay を使うとレスポンスを恣意的に遅延させることが可能です。

import import java.util.concurrent.TimeUnit;
...
    @Test
    public void qiitaDelayTest() {
        String json = "{\"message\": \"10秒遅れて来ます\"}";
        MockResponse mockResponse = new MockResponse();
        mockResponse.setBody(json);
        mockResponse.setBodyDelay(10, TimeUnit.SECONDS);
        mockResponse.setResponseCode(200);
        this.mockWebServer.enqueue(mockResponse);
        ...        
    }

リクエスト送信処理の中でタイムアウトの設定をして、わざと閾値を超えた時のエラーハンドリングを試すことができます。

Unitテストではこの様に明示的にjsonを作ってリクエストを送信する方法でテストを実行していますが、MockServerを裏で立ち上げておいて、リクエストの送信先をMockServerに向けたアプリケーションを動かして、動作を確認することも可能です。

最後に

MockWebServerのおかげで簡単にresponse指定で動作確認ができました。
テストコード自体が不慣れだったので間違いもあるかもしれないですが、外部APIを経由するロジックの動作を自分なりに検証した時の方法でした。