1. 始める前に
これまでの Codelab では、ビジネス ロジックを処理するために ViewModel
を使用する方法と、リアクティブ UI のために LiveData
を使用する方法を学びました。この Codelab では、ViewModel
コードが適切に動作していることを確認するために単体テストを作成する方法を学びます。
前提条件
- Android Studio でテスト ディレクトリを作成している
- Android Studio で単体テストとインストルメンテーション テストを作成している
- Android プロジェクトに Gradle の依存関係を追加している
学習内容
ViewModel
とLiveData
の単体テストを作成する方法。
必要なもの
- Android Studio がインストールされているパソコン
- Cupcake アプリの解答コード。
この Codelab のスターター コードをダウンロードする
この Codelab では、以前の解答コードの Cupcake アプリにインストルメンテーション テストを追加します。
この Codelab のコードを取得して Android Studio で開く手順は以下のとおりです。
コードを取得する
- 指定された URL をクリックします。プロジェクトの GitHub ページがブラウザで開きます。
- ブランチ名と Codelab で指定したブランチ名が一致していることを確認します。たとえば、次のスクリーンショットでは、ブランチ名は main です。
- プロジェクトの GitHub ページで、[Code] ボタンをクリックすると、ポップアップが表示されます。
- ポップアップで、[Download ZIP] をクリックして、プロジェクトをパソコンに保存します。ダウンロードが完了するまで待ちます。
- パソコンに保存したファイルを見つけます([ダウンロード] フォルダなど)。
- ZIP ファイルをダブルクリックして展開します。プロジェクト ファイルが入った新しいフォルダが作成されます。
Android Studio でプロジェクトを開く
- Android Studio を起動します。
- [Welcome to Android Studio] ウィンドウで、[Open] をクリックします。
注: Android Studio がすでに開いている場合は、メニューから [File] > [Open] を選択します。
- ファイル ブラウザで、展開したプロジェクト フォルダがある場所([ダウンロード] フォルダなど)に移動します。
- そのプロジェクト フォルダをダブルクリックします。
- Android Studio でプロジェクトが開くまで待ちます。
- 実行ボタン をクリックして、アプリをビルドし、実行します。期待どおりにビルドされることを確認します。
2. スターター アプリの概要
Cupcake アプリは、カップケーキの数量について 3 つのオプションを備えた注文画面を表示するホーム画面で構成されています。オプションをクリックするとフレーバーを選択する画面になり、注文品の受け取り日を選択する画面になります。その後、注文を別のアプリに送信できます。注文はどの段階でもキャンセルできます。
3.単体テスト ディレクトリを作成する
これまでの Codelab で行ったように、Cupcake アプリの単体テスト ディレクトリを作成します。
4. 単体テストクラスを作成する
ViewModelTests.kt という新しいクラスを作成します。
5. 必要な依存関係を追加する
プロジェクトに次の依存関係を追加します。
testImplementation 'junit:junit:4.+'
testImplementation 'androidx.arch.core:core-testing:2.1.0'
次に、プロジェクトを同期します。
6. ViewModel テストを作成する
簡単なテストから始めましょう。デバイスまたはエミュレータでアプリを操作するとき最初にすることは、カップケーキの数量を選択することです。そこで、まず OrderViewModel
の setQuantity()
メソッドをテストし、quantity
LiveData
オブジェクトの値を確認します。
ここでテストする quantity
変数は LiveData
のインスタンスです。LiveData
オブジェクトをテストするには追加の手順が必要です。ここで、追加した依存関係が機能します。値が変更されるとすぐに UI を更新するために、LiveData
を使用します。UI は「メインスレッド」というもので動作します。スレッド化と同時実行についてよく知らなくても問題ありません。他の Codelab で詳しく説明します。当面の間、Android アプリのコンテキストでは、メインスレッドを UI スレッドとして考えてください。ユーザーに UI を表示するコードは、このスレッドで実行されます。特に指定がない限り、単体テストはすべてがメインスレッドで実行されることを想定しています。ただし、LiveData
オブジェクトはメインスレッドにアクセスできないため、LiveData
オブジェクトがメインスレッドを呼び出してはならないことを明示的に記述する必要があります。
LiveData
オブジェクトがメインスレッドを呼び出さないように指定するには、LiveData
オブジェクトをテストするたびに特定のテストルールを提供する必要があります。
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
- これで、
quantity_twelve_cupcakes()
という関数を作成できるようになりました。メソッドで、OrderViewModel.
のインスタンスを作成します。 - このテストでは、
setQuantity
が呼び出されたときに、OrderViewModel
のquantity
オブジェクトが更新されることを確認します。ただし、メソッドを呼び出す前、またはOrderViewModel
のデータを扱う前に、変更を出力するにはLiveData
オブジェクトの値をテストするときにオブジェクトを監視する必要がある点に注意することが重要です。これを行う簡単な方法は、observeForever
メソッドを使用することです。quantity
オブジェクトでobserveForever
メソッドを呼び出します。このメソッドにはラムダ式が必要ですが、ラムダ式は空のままにしておくことができます。 - 次に、パラメータとして
12
を渡してsetQuantity()
メソッドを呼び出します。
val viewModel = OrderViewModel()
viewModel.quantity.observeForever {}
viewModel.setQuantity(12)
quantity
オブジェクトの値は12
であると推測できます。なお、LiveData
オブジェクトは値そのものではありません。値は、value
というプロパティに含まれています。次のアサーションを作成します。
assertEquals(12, viewModel.quantity.value)
テストは次のようになります。
@Test
fun quantity_twelve_cupcakes() {
val viewModel = OrderViewModel()
viewModel.quantity.observeForever {}
viewModel.setQuantity(12)
assertEquals(12, viewModel.quantity.value)
}
テストを実行します。これで、最新の Android 開発に欠かせないスキルである、最初の LiveData
単体テストを作成できました。このテストではビジネス ロジックはあまりテストされないため、もう少し複雑なテストを作成しましょう。
OrderViewModel
の主な機能として、注文価格の計算があります。これは、カップケーキの数量を選択したときと、受け取り日を選択したときに行われます。価格計算はプライベート メソッドで行われるため、テストでこのメソッドを直接呼び出すことはできません。このメソッドは、OrderViewModel
の他のメソッドだけが呼び出せます。こうしたメソッドは公開されているため、価格計算をトリガーするために呼び出して、価格の値が期待どおりであることを確認できます。
おすすめの方法
カップケーキの数量が選択されたときと、日付が選択されたときに、価格が更新されます。いずれもテストする必要がありますが、通常は 1 つの機能だけをテストすることをおすすめします。そのため、テストごとに別々のメソッドを作成します(数量が更新されたときに価格をテストする関数と、日付が更新されたときに価格をテストする関数)。あるテストが失敗したために別のテストが失敗するということがあってはなりません。
price_twelve_cupcakes()
というメソッドを作成し、テストとしてアノテーションを付けます。- このメソッドで
OrderViewModel
のインスタンスを作成し、パラメータとして 12 を渡してsetQuantity()
メソッドを呼び出します。
val viewModel = OrderViewModel()
viewModel.setQuantity(12)
OrderViewModel
のPRICE_PER_CUPCAKE
を見ると、カップケーキは 1 つ $2.00 であることがわかります。また、ViewModel
が初期化されるたびにresetOrder()
が呼び出され、このメソッドでは、デフォルトの日付は今日の日付であり、PRICE_FOR_SAME_DAY_PICKUP
は $3.00 であることがわかります。そのため、12 * 2 + 3 = 27 となります。12 個のカップケーキを選択した後のprice
変数の値は、$27.00 になると予想されます。そこで、期待値である $27.00 がprice LiveData
オブジェクトの値に等しいというアサーションを作成しましょう。
assertEquals("$27.00", viewModel.price.value)
テストを実行します。
これは失敗するはずです。
テスト結果では、実際の値は null
となっています。これには理由があります。OrderViewModel
の price
変数を見てみると、次のようになっています。
val price: LiveData<String> = Transformations.map(_price) {
// Format the price into the local currency and return this as LiveData<String>
NumberFormat.getCurrencyInstance().format(it)
}
この例は、テストで LiveData
を監視すべき理由を示しています。price
の値は、Transformation
を使用して設定されます。このコードは基本的に、price
に割り当てられた値を受け取って通貨形式に変換します。そのため、手動で行う必要はありません。ただし、このコードには他の意味もあります。LiveData
オブジェクトを変換する際、どうしても必要な場合を除き、このコードは呼び出されません。そうすることでモバイル デバイスのリソースを節約します。このコードは、オブジェクトの変更を監視する場合にのみ呼び出されます。もちろん、これはアプリで行われますが、テストでも同じことを行う必要があります。
- テストメソッドで、数量を設定する前に次の行を追加します。
viewModel.price.observeForever {}
テストは次のようになります。
@Test
fun price_twelve_cupcakes() {
val viewModel = OrderViewModel()
viewModel.price.observeForever {}
viewModel.setQuantity(12)
assertEquals("$27.00", viewModel.price.value)
}
これで、テストを実行すると合格するはずです。
7. 解答コード
8. 完了
この Codelab では次のことを行いました。
LiveData
テストの設定方法を学習しました。LiveData
自体をテストする方法を学習しました。- 変換された
LiveData
をテストする方法を学習しました。 - 単体テストで
LiveData
を監視する方法を学習しました。