OkHttpのMockWebServerをJUnitで利用する

# About

JavaでHTTPクライアントのテストを書こうとしていたら、[okhttp/mockwebserver](https://github.com/square/okhttp/tree/master/mockwebserver)なるものを見つけたのですが、これが便利だったので紹介します。

サンプルコードの一部は、GitHubにあります。

https://github.com/hosopy/OkHttpMockSample

# 題材

RestAPIクライアントの実装に[Retrofit](http://square.github.io/retrofit/)を利用する状況を想定し、サンプル題材としてGitHub APIにアクセスするクライアントを考えます。

```java:GitHubService.java
public interface GitHubService {
    /**
     * 指定されたユーザのリポジトリリストを取得.
     * 
     * @param user
     * @reeturn Call<List<Repository>>
     */
    @GET("/users/{user}/repos")
    Call<List<Repository>> listRepos(@Path("user") String user);
}
```

GitHubServiceの使い方としては、次のようなコードになると思います。
当然ですが、本物のGitHub APIのURLをRetrofitのbaseUrlに設定します。

```java
final Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
final GitHubService service = retrofit.create(GitHubService.class);
final Call<List<Repository>> listRepos = service.listRepos("hosopy");

listRepos.enqueue(new Callback<List<Repository>>() {
    @Override
    public void onResponse(Response<List<Repository>> response, Retrofit retrofit) {
    }

    @Override
    public void onFailure(Throwable throwable) {
    }
});
```

# GitHubServiceをテストする

JUnitでGitHubServiceのテストを書きたいとします。

テストなので、本物のGitHub APIにアクセスさせることは避けたいのですが、MockWebServerを利用することで、Webサーバのモックを用意することが出来ます。

MockWebServerを利用して、GitHubServiceをテストするコードはこのように書くことが出来ます。
(Matcherにhamcrest-allを使っています)

```java:GitHubServiceTest.java
@RunWith(JUnit4.class)
public class GitHubServiceTest extends TestCase {

    private static final int TIMEOUT = 10000;

    @Test(timeout = TIMEOUT)
    public void testListReposSuccess() throws IOException, InterruptedException {
        final BlockingQueue<String> events = new LinkedBlockingQueue();

        // MockWebServerとMockResponseを設定
        final MockWebServer mockWebServer = new MockWebServer();
        final MockResponse mockResponse = new MockResponse()
                .setResponseCode(200)
                .setHeader("Content-Type", "application/json")
                .setBody("[{\"name\":\"Bolts-iOS-Sample\", \"html_url\":\"https://github.com/hosopy/Bolts-iOS-Sample\"}," +
                        "{\"name\":\"dotfiles\",\"html_url\":\"https://github.com/hosopy/dotfiles\"}]");
        mockWebServer.enqueue(mockResponse);
        mockWebServer.start();

        // baseUrlにMockWebServerのURLを設定
        final Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(mockWebServer.url("/").toString())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        final GitHubService service = retrofit.create(GitHubService.class);
        final Call<List<Repository>> listRepos = service.listRepos("hosopy");

        // テスト
        listRepos.enqueue(new Callback<List<Repository>>() {
            @Override
            public void onResponse(Response<List<Repository>> response, Retrofit retrofit) {
                for (Repository repository : response.body()) {
                    events.offer(repository.getName());
                    events.offer(repository.getHtmlUrl());
                }
            }

            @Override
            public void onFailure(Throwable throwable) {
            }
        });

        assertThat(events.take(), is("Bolts-iOS-Sample"));
        assertThat(events.take(), is("https://github.com/hosopy/Bolts-iOS-Sample"));
        assertThat(events.take(), is("dotfiles"));
        assertThat(events.take(), is("https://github.com/hosopy/dotfiles"));

        mockWebServer.shutdown();
    }
}
```

MockWebServerは他にも色々な使い方が出来るようなので、[GitHubのドキュメント](https://github.com/square/okhttp/tree/master/mockwebserver)を参照してみて下さい。