/robolectric-espresso-samples

Robolectric UI Test Samples by using Espresso Idling Resource

Primary LanguageKotlinApache License 2.0Apache-2.0

Robolectric UI Test Samples by Using Espresso Idling Resource

DroidKaigi 2020 で発表する予定だった「Robolectricの限界を理解してUIテストを高速に実行しよう」のサンプルコードです。

本リポジトリは、 GoogleがAndroid Jetpackのサンプルアプリとして開発しているAndroid Sunflowerに、Robolectricで動作するEspressoで書かれたテストコードを追加したものです。

あわせて、本リポジトリには、EspressoのIdlingResourceをサポートするように改造したRobolectricが含まれています。 IdlingResource対応のRobolectricを試してみたい方は、 後述の「IdlingResource対応のRobolectricを試してみるには」を参照してください。

Android Sunflower付属のオリジナルのREADMEは、README.orig.mdを参照してください。

動作確認環境

  • Android Studio 3.5.3

オリジナルのAndroid Sunflowerとの違い

  • ライブラリの依存関係を最新化しています。 それに伴い、最新のライブラリでビルドできるようにPlantDetailFragment.ktMaskedCardView.ktを修正しています。
  • オリジナルのREADME.mdのファイル名をREADME.orig.mdにリネームしています。
  • 既存のテストを削除しています。
  • Espressoで書いたUIテストをsrc/androidTestsrc/testの両方に追加しています。
  • Instrumented TestからもLocal Testからも参照できるsrc/sharedTestディレクトリを追加しています。
  • 植物が庭に追加されるときに、わざとバックグラウンドスレッドで3秒スリープするように変更しています。 また、そのときに実行されるスレッドをテストコードから変更できるようにしています。
    (PlantDetailViewModel.kt)
  • 一度保持したAppDatabaseインスタンスを、テストコードから破棄できるようにしています。
    (AppDatabase.kt)
  • 一度保持したDAOインスタンスを、テストコードから差し替えられるようにしています。 (GardenPlantingRepository.ktPlantRepository.kt)

テストコードの概要

Espresso Test Recorderで記録したテスト(一部改変あり)を、 Instrumented TestRobolectricを使ったLocal Testの両方で動作するようにしています。 テストの内容は次の通りで、Mangoを選ぶものとEggplantを選ぶものの2つのケースが存在しています。

  1. Add Plantを押してPlant List画面に遷移する
  2. リストされている植物からを1つ選んで、FABを押して追加する
  3. My Garden画面に戻って、追加した植物が表示されていることを確認する。

テストのエントリーポイントは次の通りです。

実際にEspressoのAPIを使って画面を操作している部分はPage Object化し、両方のテストから参照できる
src/sharedTest/java/com/google/samples/apps/sunflower/{page,util}/
配下に配置しています。

IdlingResource対応のRobolectricを試してみるには

本リポジトリには、EspressoのIdlingResourceをサポートするように改造したRobolectricが含まれています。 ご自身のプロジェクトに、IdlingResource対応のRobolectricを適用するには、次の手順にしたがってください。

ファイルのコピー

次のディレクトリを、本リポジトリから、適用したいプロジェクトにコピーしてください。 コピー先も同じディレクトリ構成にしてください。

  • app/local-repo
  • app/src/test/resources
  • app/src/test/java/androidx

build.gradleファイルの編集

  1. app/local-repoを、mavenのリポジトリ参照先として追加してください

    // app/build.gradleに追記する場合
    repositories {
        maven { url = file('local-repo') }
    }
  2. 依存関係に宣言されているRobolectricのバージョン表記を4.3.1-modifiedにしてください

    dependencies {
        ...
        testImplementation "org.robolectric:robolectric:4.3.1-modified"
        ...
    }

以上で、IdlingResourceに対応したRobolectricが使えるようになります。

Idling Resource対応についての注意事項

  • この対応は限られたケースで動作確認したに過ぎません。その点ご理解の上お試しください。
  • IdlingRegistry.registerLooperAsIdlingResource()を使ったケースは未確認です。恐らく対応できていないと思います。

Robolectric対応のポイント

RobolectricのLooper Mode

Robolectric 4.3から導入されたPAUSED Looper Modeにしています。 Looper Modeについての詳細はImproving Robolectric's Looper simulationを参照してください。

@RunWith(AndroidJUnit4::class)
@LooperMode(LooperMode.Mode.PAUSED)
class RobolectricGardenActivityTest2 {
    ...
}

WorkManager対応

Robolectricで動かす場合、デフォルトのinitializerでは動作しません。 そのため、デフォルトのinitializerを削除し、WorkManagerTestInitHelperを使って初期化しています。

デフォルトのinitializerを削除している箇所はsrc/test/AndroidManifest.xmlです。

初期化コードは、テスト専用のアプリケーションクラスTestApplicationを定義し、そのonCreate()の中で実行しています。 Robolectricでは、テスト専用のアプリケーションクラスを次のように指定することができます。

@Config(application = TestApplication::class)
class RobolectricGardenActivityTest2 {
    ...
}    

Idling Resource対応

Robolectricの現バージョンでは、EspressoのIdling Resourceに対応していません。そのため、このサンプルでは独自実装によってRobolectricでIdling Resourceを待ち合わせるようにしてあります。

EspressoでIdling Resourceがアイドル状態になるのを待ち合わせている箇所はUiControllerインターフェイスを実装したUiControllerImplです。

一方で、Robolectricが提供しているUiControllerインターフェイスの実装はLocalUiControllerで、こちらにはIdling Resourceを待ち合わせているコードがありません。

そこでLocalUiControllerを拡張したIdlingLocalUiControllerを実装し、RobolectricでもIdling Resourceを待ち合わせるようにしました。

具体的には、EspressoのUiControllerImplのうち、Idling Resourceを待ち合わせているロジックだけをIdlingLocalUiControllerに移植しています。その差分はこのコミットを参照してください。

なお、Robolectricが提供するUiControllerインターフェイスの実装は、JARファイルの META-INF/services/androidx.test.platform.ui.UiControllerにハードコードされているため、RobolectricのJARファイルにも手を加えざるを得ませんでした。

手を加えたRobolectricはapp/local-repoディレクトリ配下に格納しています。 オリジナルとの差分はMETA-INF/services/androidx.test.platform.ui.UiControllerを削除した点のみです。
(後日、Robolectric本家にPRできればと考えています)

この対応のための修正は、 #2 にまとまっていますので、興味のある方は参考にしてみてください。

注意事項

このIdlingResource対応は限られたケースで動作確認したに過ぎません。 前述の「Idling Resource対応についての注意事項」をご理解の上お試しください。

Room対応

Robolectricでは、テスト独立性を高めるために、テスト終了時にデータベースファイルを削除する仕様となっています。

そのため、ビルドしたRoomDatabaseのインスタンスを、テストをまたがって保持する設計になっている場合、 2回目のテストからは存在しないデータベースファイルを参照することになり、データベースアクセスが正しく動作しません。

次のようにすることで、このRobolectricの仕様に対応することができます。 この対応にはアプリ側にも手を入れざるを得ないケースが多いと思います。

  • Robolectric用に用意したApplicationクラスで、毎回RoomDatabaseをビルドするようにします。
  • アプリケーション内で以下の参照を保持し続ける実装になっている場合は、RoomDatabaseをビルドしたタイミングで、新しい参照に更新するようにします。
    • ビルドしたRoomDatabaseのインスタンス
    • DAOのインスタンス

具体的な修正内容は #3 のうち、以下の箇所を参照してください。

  • AppDatabase.kt
  • GardenPlantingRepository.kt
  • PlantRepository.kt
  • RobolectricGardenActivityTest2.kttearDown()appDatabaseclose()clear()を呼び出している箇所

Robolectricで動作しない機能について

次の機能は、Robolectricでは動作しないことが確認できています。 ここに挙げた機能を使った部分については、Robolectricによるテストを避けた方が無難です。

ライセンス

Original Copyright 2018 Google, Inc. See README.orig.md for details.

Modifications Copyright 2020 TOYAMA Sumio <jun.nama@gmail.com>

Licensed under the Apache License, Version 2.0.

Third Party Content