Android でテストダブルを使用する

要素または要素のシステムをテストする場合は、個別にテストします。たとえば、ViewModel をテストするために、エミュレータを起動して UI を起動する必要はありません。これは、ViewModel が Android フレームワークに依存していないためです(依存すべきでもありません)。

ただし、テスト対象の機能が動作するために他の機能に依存している場合があります。たとえば、ViewModel はデータ リポジトリに依存して動作する場合があります。

テスト対象に依存関係を提供する必要がある場合は、テストダブル(またはテストオブジェクト)を作成するのが一般的です。テスト ダブルは、アプリ内のコンポーネントのように見え、動作するオブジェクトですが、特定の動作やデータを提供するためにテスト内で作成されます。主な利点は、テストをより迅速かつ簡単にできることです。

テストダブルの種類

テスト ダブルにはさまざまな種類があります。

偽装 クラスの「動作する」実装を持つテストダブル。テストには適しているが、本番環境には適さない方法で実装されています。

例: インメモリ データベース。

フェイクはモック フレームワークを必要とせず、軽量です。推奨されます。

モック プログラムされたとおりに動作し、インタラクションに関する期待値を持つテストダブル。モックのインタラクションが定義した要件と一致しない場合、モックのテストは失敗します。通常、モックはモック フレームワークを使用して作成され、これらのすべてを実現します。

例: データベース内のメソッドが 1 回だけ呼び出されたことを確認します。

スタブ テストダブルは、プログラムされたとおりに動作しますが、インタラクションに関する期待値はありません。通常は、モック フレームワークを使用して作成します。単純にするため、スタブよりもフェイクを優先します。
ダミー 渡されるが使用されないテストダブル(パラメータとして指定する必要がある場合など)。

例: クリック コールバックとして渡される空の関数。

スパイ 実際のオブジェクトをラップし、モックと同様に追加情報を追跡します。通常は複雑さを増すため使用されません。そのため、スパイよりもフェイクまたはモックが推奨されます。
シャドウ Robolectric で使用される偽のクラス。

フェイクを使用した例

UserRepository というインターフェースに依存し、最初のユーザーの名前を UI に公開する ViewModel を単体テストするとします。インターフェースを実装して既知のデータを返すことで、偽のテストダブルを作成できます。

object FakeUserRepository : UserRepository {
    fun getUsers() = listOf(UserAlice, UserBob)
}

val const UserAlice = User("Alice")
val const UserBob = User("Bob")

この偽の UserRepository は、本番環境バージョンで使用するローカル データソースとリモート データソースに依存する必要はありません。このファイルはテストソースセットに存在し、本番環境のアプリには同梱されません。

フェイク依存関係を使用すると、リモート データソースに依存せずに既知のデータを返すことができます。
図 1: 単体テストの偽の依存関係。

次のテストでは、ViewModel が最初のユーザー名をビューに正しく公開することを確認します。

@Test
fun viewModelA_loadsUsers_showsFirstUser() {
    // Given a VM using fake data
    val viewModel = ViewModelA(FakeUserRepository) // Kicks off data load on init

    // Verify that the exposed data is correct
    assertEquals(viewModel.firstUserName, UserAlice.name)
}

ViewModel はテスターによって作成されるため、単体テストでは UserRepository を偽物に置き換えることができます。ただし、大規模なテストで任意の要素を置き換えるのは難しい場合があります。

コンポーネントの置き換えと依存関係の注入

テストでテスト対象システムの作成を制御できない場合、テストダブルのコンポーネントの置き換えが複雑になり、アプリのアーキテクチャがテスト可能な設計に従う必要があります。

大規模なエンドツーエンド テストでも、アプリの完全なユーザーフローをナビゲートするインストルメンテーション UI テストなど、テストダブルを使用するとメリットがあります。その場合は、テストを完全なものにすることをおすすめします。気密性テストでは、インターネットからのデータの取得など、すべての外部依存関係を回避します。これにより、信頼性とパフォーマンスが向上します。

図 2: アプリの大部分をカバーし、リモートデータを偽装する大規模なテスト。

この柔軟性を手動で実現するようにアプリを設計することもできますが、Hilt などの依存関係の挿入フレームワークを使用して、テスト時にアプリ内のコンポーネントを置き換えることをおすすめします。Hilt テストガイドをご覧ください。

次のステップ

[テスト戦略] ページでは、さまざまな種類のテストを使用して生産性を向上させる方法について説明します。