LoginSignup
47
45

More than 5 years have passed since last update.

Dagger2とRetrofitを使うAndroid appをJVM unit testingしてみた

Last updated at Posted at 2015-03-21

追記(2015/19/7)

Robolectric 3.0以降はJVM unit testingの仕組みを利用するようになりました。素の状態よりテストしやすいのでこちらを利用するとよさそうです。このエントリで例に上げたHeliumもRobolectricに移行しました。

三行まとめ

  • Android Studio 1.1からJVM unit testingがサポートされた
  • Android frameworkにあまり依存しないロジックであれば有用
  • テストファイル書き換えからテスト実行まで数秒でおわるので試行錯誤しやすい

説明

Android SDKがJVMによるテストをサポートしたという話があります:

Robolectric を使わずとも、公式のツールキットを使うだけでJVM unit testingができるというのは画期的で、今後モデルまわりのテストはこっちに行くのかなと思っています。

ただし、まだAndroid Studioで通常のandroidTestとの併用は難しいですし、テストレポートをAndroid Studioで見ることすらできません。プロダクトに取り入れる段階ではないでしょう。

またAndroid frameworkは一切使えません。Android frameworkのクラスライブラリを参照することはできるのですが、何か機能を呼ぼうとすると軒並みthrow new RuntimeException("Method foo not mocked") となります。

ともあれ、どんなものかは試しておきたいところ。というわけでテストを書いてみました。

もともとのアプリは以下のような特徴をもっています。

  • RetrofitでWeb APIにアクセスする
  • RetrofitのRxJavaインターフェイスを使用
  • Dagger 2.0 (2.0-SNAPSHOT) でDependency Injection
  • JUnit4でテストを書いている
  • Circle-CI + DockerでCI

いざテストを書いてみると、DIをしているのでmockするのは難しくありませんでした。RetrofitはHTTP clientを retrofit.client.Client というインターフェイスで抽象化しているので、mockwebserverすら必要ありません。 Client#execute() のmockを実装するだけです。

  class MockClient implements Client {

        final String path;

        final String assetName;

        final String contentType;

        MockClient(String path, String assetName, String contentType) {
            this.path = path;
            this.assetName = assetName;
            this.contentType = contentType;
        }

        @Override
        public Response execute(Request request) throws IOException {
            URI uri = URI.create(request.getUrl());
            if (uri.getPath().equals(path)) {
                return resourceFoundInXml(request.getUrl());
            } else {
                return resourceNotFound(request.getUrl());
            }
        }

        Response resourceNotFound(String uri) {
            return new Response(uri, 404, "not found", new ArrayList<Header>(),
                    new TypedByteArray(contentType, new byte[]{}));
        }

        Response resourceFoundInXml(String uri) throws IOException {
            return new Response(uri, 200, "ok", new ArrayList<Header>(),
                    new TypedByteArray(contentType, getAssetFileInBytes(assetName)));

        }
    }

    @Test
    public void testRequestHotentries() throws Exception {
        HatebuFeedClient feedClient = new HatebuFeedClient(
                new MockClient("/hatena/b/hotentry", "hotentries.rss", "application/xml"),
                new MockRequestIntercepter());

        List<HatebuEntry> entry = feedClient.getHotentries().toBlocking().single();
        assertThat(entry, hasSize(greaterThan(0)));
    }

なお、アプリ実装のClientのproviderはOkHttpを使うもので、以下のようになっています。

    @Provides
    public Client provideRetrofitClient(OkHttpClient httpClient) {
        return new OkClient(httpClient);
    }

Android frameworkがまったく使えないので、MockContextを駆使したり
Log.d() の代わりに System.out.println() を使ったりと、通常のandroidTest以上にpure Javaよりに意識を切り替える必要はあります。モデルやAPI通信部分、ユーティリティであればかろうじてテストできるという感じでしょうか。

しかしながら、テストを実行する時間が非常に短いというのは大きなメリットで、試行錯誤しながらロジックを書いていくというケースでは今後使っていきたいという印象です。

47
45
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
45