Retrofit2のレスポンスをmockに置き換えるMockWebServer編
ユーザー登録画面など、ネットワーク通信を伴う画面のUIテストするとき、実際のサーバへアクセスしては、テストが意図した結果にならないことがあるだろう。
MockWebServerを使用することで、サーバへのアクセスを回避し、かつ、必要なレスポンスを返すようにMockを使用することで正しく動作のテストを行える。
MockWebServerを使う準備
build.gradleに以下の記述を追加
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.3.0'
Testを書く
MockWebServerを使用するために、アクセス先のベースURLをMockWebServerのURLに変更する。
テストではRetrofitを使用し、Qiitaの新着投稿を取得するサービスを作成し、その結果をMockWebServerから受け取る。
MockWebServerのインスタンスを作成し、start()メソッドをコールするだけで、Mockサーバの準備は完了する。
レスポンスはテスト毎にenqueueメソッドをコールし、MockResponseをセットする。
セットしないとレスポンスは返却されず、タイムアウトとなる。
1つのテストで複数のWebアクセスを行う場合は、setDispatcher()メソッドにてDispatcherをセットし、複数のレスポンスを返却できるように実装する。
インターフェース
public interface IQiitaService { @GET("/api/v1/items") Call<ResponseBody> getNewPosts(); }
インターフェースの実体
テストのためにインスタンス取得時にベースURLを渡す実装とする
public class QiitaService { private IQiitaService mService; public static QiitaService getInstance(String url) { return new QiitaService(url); } private QiitaService(String url) { init(url); } public static final String BASE_URL = "https://qiita.com"; private void init(String url) { Retrofit retrofit = new Retrofit.Builder().baseUrl(url) .build(); mService = retrofit.create(IQiitaService.class); } public interface ResponseCallback { void onResponse(Response<ResponseBody> response); } public void getNewlyPost(final ResponseCallback callback) { Call call = mService.getNewPosts(); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { callback.onResponse(response); } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { callback.onResponse(null); } }); } }
テストケース
public class QiitaServiceTest extends InstrumentationTestCase { private MockWebServer mMockWebServer; public void setUp() throws Exception { super.setUp(); mMockWebServer = new MockWebServer(); mMockWebServer.start(11262); } public void tearDown() throws Exception { super.tearDown(); mMockWebServer.shutdown(); } private static final String RESPONSE = "[{\"id\": 1,\n" + " \"uuid\": \"1a43e55e7209c8f3c565\",\n" + " \"user\":\n" + " {\"name\": \"Hiroshige Umino\",\n" + " \"url_name\": \"yaotti\",\n" + " \"profile_image_url\": \"https://si0.twimg.com/profile_images/2309761038/1ijg13pfs0dg84sk2y0h_normal\" },\n" + " \"title\": \"てすと\",\n" + " \"body\": \"<p>foooooooooooooooo</p>\\n\",\n" + " \"created_at\": \"2012-10-03 22:12:36 +0900\",\n" + " \"updated_at\": \"2012-10-03 22:12:36 +0900\",\n" + " \"created_at_in_words\": \"18 hours ago\",\n" + " \"updated_at_in_words\": \"18 hours ago\",\n" + " \"tags\":\n" + " [{\"name\": \"FOOBAR\",\n" + " \"url_name\": \"FOOBAR\",\n" + " \"icon_url\": \"http://qiita.com/icons/thumb/missing.png\",\n" + " \"versions\": ['1.2', '1.3']}],\n" + " \"stock_count\": 0,\n" + " \"stock_users\": [],\n" + " \"comment_count\": 0,\n" + " \"url\": \"http://qiita.com/items/1a43e55e7209c8f3c565\",\n" + " \"gist_url\": null,\n" + " \"tweet\": false,\n" + " \"private\": false,\n" + " \"stocked\": false\n" + "},\n" + "]\n"; public void test_200() throws Throwable { mMockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(RESPONSE)); final CountDownLatch latch = new CountDownLatch(1); QiitaService.getInstance(mMockWebServer.url("/").toString()).getNewlyPost(new QiitaService.ResponseCallback() { @Override public void onResponse(Response<ResponseBody> response) { try { assertEquals(200, response.code()); Log.v("tag", response.body().string()); } catch (IOException e) { e.printStackTrace(); } latch.countDown(); } }); latch.await(); } public void test_404() throws Throwable { mMockWebServer.enqueue(new MockResponse().setResponseCode(404)); final CountDownLatch latch = new CountDownLatch(1); QiitaService.getInstance(mMockWebServer.url("/").toString()).getNewlyPost(new QiitaService.ResponseCallback() { @Override public void onResponse(Response<ResponseBody> response) { assertEquals(404, response.code()); latch.countDown(); } }); latch.await(); } }
Dispacherを使用する例
mMockWebServer.setDispatcher(new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { String path = request.getPath(); if("/api/v1/items".equals(path)) { return new MockResponse().setResponseCode(200).setBody(RESPONSE); } else { return new MockResponse().setResponseCode(404); } } });
参照URL:
okhttp/mockwebserver at master · square/okhttp · GitHub
Qiita API documentation - Qiita:Developer
Retrofit