visible true

技術的なメモを書く

Mockito1.9.5+OkHttp2.1.0でリクエストをmockする

OkHttpのリクエスト周りのmockingが結構面倒くさかったのでメモ。

やりたい事

ベタ書きすると以下の様な事がしたい。単純にレスポンスをmockして返却したいデータをセットしたい。

OkHttpClient okHttpClient = mock(OkHttpClient.class);
String body = "hoge";

ResponseBody responseBody = mock(ResponseBody.class);
when(responseBody.string()).thenReturn(body);

Response response = mock(Response.class);
when(response.isSuccessful()).thenReturn(true);
when(response.body()).thenReturn(responseBody);

Call call = mock(Call.class);
when(call.execute()).thenReturn(response);
when(okHttpClient.newCall(any(Request.class))).thenReturn(call);

OkHttpClientはinjectして使う。テスト対象では以下のようなコードを実行している。

Request request = new Request.Builder()
        .url(url)
        .build();
Response response = client.newCall(request).execute();
if (response != null) {
  if (response.isSuccessful()) {
    String body = response.string();
    //...
  }
}
//...

問題

ところが、ResponseBodyは各種メソッドがfinalなのでmockできない。Responseはfinal classなのでmockできない。このため上記のコードでは動かない。

環境

環境としては以下となる。robolectricを使っている場合なので、Androidでテストを動かす場合はdexmaker等を追加する必要がある。

dependencies {
    compile 'com.squareup.okhttp:okhttp:2.1.0'

    androidTestCompile('org.mockito:mockito-core:1.9.5') {
        exclude group: 'org.hamcrest'
        exclude module: 'objenesis'
    }
}

テストコード

OkHttpの最新だとResponseBody.Builderというのがあるらしい、しかしOkHttp2.1.0ではない。という事でResponseBodyを実装しなければならない。ResponseはResponce.Builderがあるのでこれを使って作成する。Callはmock出来るので特に対応はいらない。

@Test
public void mockingOkHttp() throws Exception {
  OkHttpClient okHttpClient = mock(OkHttpClient.class)
  File file = new File("test.html");
  final long length = file.length();
  final InputStream in = new FileInputStream(file);

  ResponseBody responseBody = new ResponseBody() {
    @Override
    public MediaType contentType() {
      return MediaType.parse("text/html");
    }

    @Override
    public long contentLength() {
      return length;
    }

    @Override
    public BufferedSource source() {
      return Okio.buffer(Okio.source(in));
    }
  };
  Response response = new Response.Builder()
      .code(200)
      .body(responseBody)
      .protocol(Protocol.HTTP_1_0)
      .request(new Request.Builder()
          .url("")
          .build())
      .build();
  
  Call call = mock(Call.class);
  when(call.execute()).thenReturn(response);
  when(okHttpClient.newCall(any(Request.class))).thenReturn(call);

  //
}

まとめ

ザックリ最低限のコードを書いた。これらをラップしたりして色々やると簡単にmockできる仕組みが作れると思う。しかしなんでResponseBodyやResponseはfinalなんだろうめんどくさい