1. 始める前に
以前の Codelab で、単体テストの作成して実行する方法を学習しました。この Codelab ではインストルメンテーション テストに焦点を当て、その動作と作成方法を学習します。
前提条件
- Android Studio でプロジェクトを作成したことがある。
- Android Studio でコードを記述した経験がある。
- Android Studio でプロジェクトを操作した経験がある。
- Android Studio で簡単な単体テストを作成したことがある。
学習内容
- インストルメンテーション テストの動作
- インストルメンテーション テストの実行方法
- インストルメンテーション テストの作成方法
必要なもの
- Android Studio がインストールされているパソコン
- Tip Time アプリの解答コード
この Codelab のスターター コードをダウンロードする
この Codelab では、以前の解答コードを基にして、Tip Time アプリにインストルメンテーション テストを追加します。
- プロジェクト用に提供されている GitHub リポジトリ ページに移動します。
- ブランチ名が Codelab で指定されたブランチ名と一致していることを確認します。たとえば、次のスクリーンショットでは、ブランチ名は main です。
- プロジェクトの GitHub ページで、[Code] ボタンをクリックすると、ポップアップが表示されます。
- ポップアップで、[Download ZIP] をクリックして、プロジェクトをパソコンに保存します。ダウンロードが完了するまで待ちます。
- パソコンに保存したファイルを見つけます([ダウンロード] フォルダなど)。
- ZIP ファイルをダブルクリックして展開します。プロジェクト ファイルが入った新しいフォルダが作成されます。
Android Studio でプロジェクトを開く
- Android Studio を起動します。
- [Welcome to Android Studio] ウィンドウで、[Open] をクリックします。
注: Android Studio がすでに開いている場合は、メニューから [File] > [Open] を選択します。
- ファイル ブラウザで、展開したプロジェクト フォルダがある場所([ダウンロード] フォルダなど)に移動します。
- そのプロジェクト フォルダをダブルクリックします。
- Android Studio でプロジェクトが開かれるまで待ちます。
- 実行ボタン をクリックして、アプリをビルドし、実行します。期待どおりにビルドされることを確認します。
2. スターター アプリの概要
Tip Time アプリは 1 画面で構成されており、その画面には、請求額を入力するテキスト入力、チップの割合を選択するラジオボタン、チップを計算するボタンが表示されます。
3.インストルメンテーション テスト ディレクトリを作成する
この Codelab のスターター コードは完全に動作しますが、テストもテスト ディレクトリもありません。テストを作成するには、その前にインストルメンテーション テスト用のディレクトリを追加する必要があります。スターター コードをダウンロードしたら、以下の手順でインストルメンテーション テスト用のクラスを追加します。
- 最も簡単な手順を説明します。まず、[Android] ビューから [Project] ビューに切り替えます。それには、左上のプロジェクト ペインで [Android] というプルダウンをクリックし、[Project] を選択します。
- プロジェクト ビューは次のようになります。
- 1 つ目のプルダウンをクリックし、次に [app] -> [src] とドリルダウンします。
- src を右クリックして、[New] -> [Directory] を選択します。
- 次のようなウィンドウが表示されます。
- androidTest/java を選択します。
- プロジェクト構造内に androidTest ディレクトリが表示されます。
- java ディレクトリを右クリックし、[New] -> [Package] を選択します。
- 次のウィンドウが表示されます。
- ウィンドウに次のテキストを入力して Enter キーを押します。
com.example.tiptime
- プロジェクト ウィンドウは次のようになります。
- 最後に、com.example.tiptime を右クリックして [New] -> [Kotlin Class/File] を選択します。
- 表示されたウィンドウで「
CalculatorTests
」と入力し、プルダウンから [Class] を選択して Enter キーを押します。
4. 初めてのインストルメンテーション テストを作成する
それでは、インストルメンテーション テストの作成を始めましょう。以下の手順では、チップが 20% の場合の機能をテストします。
- すぐ前の手順で作成したファイルを開くと、次のようになっています。
package com.example.tiptime
class CalculatorTests {
}
- インストルメンテーション テストには、デバイスまたはエミュレータでのテストを可能にする InstrumentationTestRunner が必要です。インストルメンテーション ランナーはいくつかありますが、このテストでは
AndroidJUnit4
テストランナーを使用します。テストランナーを指定するには、クラスに次のようにアノテーションを付けます。
@RunWith(AndroidJUnit4::class)
class CalculatorTests {
}
念のため、必要なインポートを次に示します。
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
これで、テストロジックの記述を開始するための準備は完了です。
- Tip Time アプリは、1 つのアクティビティ
MainActivity
で構成されています。テストケースでこのアクティビティを操作するには、まずこれを起動する必要があります。CalculatorTests
クラス内に次のコードを追加します。
@get:Rule()
val activity = ActivityScenarioRule(MainActivity::class.java)
ActivityScenarioRule
は AndroidX Test ライブラリから提供されます。このルールは、デベロッパーが指定したアクティビティを起動するようデバイスに指示します。ここには、@get:Rule
アノテーションを付ける必要があります。それにより、このクラスの各テストの前に後続のルール(この場合はアクティビティの起動)を実行するよう指定できます。テストルールはテストに不可欠なツールであるため、いずれ独自のルールの作成方法を学習することになるでしょう。
- 次に、テストロジック自体を記述する必要があります。
calculate_20_percent_tip()
という名前の関数を作成し、@Test
アノテーションを付けます。
@Test
fun calculate_20_percent_tip() {
}
Espresso
このコースのインストルメンテーション テストには、主に Espresso を使用します。Espresso は、Android Studio で Android プロジェクトを作成するとすぐに使用可能になるライブラリであり、コードを介して UI コンポーネントを操作できます。
これまでに、Android Studio には多くのオートコンプリート機能があることにお気づきかもしれません。Espresso を使用するうえで厄介なのは、ライブラリがインポートされていない場合はメソッドがオートコンプリートされないという点です。そのため、ドキュメントを調べながらでないと、Espresso に用意されたメソッドを取り扱うのが難しい場合があります。そこでこのレッスンでは、全体を通してテストの完了に必要なメソッドを提示することにします。
まず、[Cost of Service] 入力テキストビューに請求額を入力するコードを記述する必要があります。[app] -> [src] -> [main] -> [res] -> [layout] -> [activity_main.xml] に移動すると、TextInputEditText
の ID が cost_of_service_edit_text
であることがわかります。この ID 名をコピーします。これは、後でテストに必要となります。
テスト関数を実装する
これで、テストクラスの calculate_20_percent_tip()
関数にテストロジックを記述できます。
- まず、
onView()
関数を使用して、操作対象の UI コンポーネント(この場合はTextInputEditText
)を見つけます。onView()
関数は、ViewMatcher
オブジェクト パラメータを受け取ります。ViewMatcher
は基本的に特定の条件に一致する UI コンポーネント(この場合は、ID がR.id.cost_of_service_edit_text
のコンポーネント)です。
関数 withId()
は、渡された ID を持つ UI コンポーネントを ViewMatcher
として返します。onView()
は、ViewInteraction
という、自分がデバイスを直接操作しているかのように操作できるオブジェクトを返します。テキストを入力するには、この ViewInteraction
に対して perform()
を呼び出します。次に、perform()
は ViewAction
オブジェクトを受け取ります。ViewAction
を返すメソッドはいくつかありますが、ここで使用するのは typeText()
メソッドです。activity_main.xml
から、チップの選択肢のデフォルトは 20% であるとわかるため、さしあたり、どの割合のチップを選択するかの指定は必要ありません。
onView(withId(R.id.cost_of_service_edit_text))
.perform(typeText("50.00"))
すると、命令全体は次のようになります。
onView(withId(R.id.cost_of_service_edit_text))
.perform(typeText("50.00"))
.perform(ViewActions.closeSoftKeyboard())
- テキストを入力したら、テスト内で [Calculate] ボタンをクリックする必要があります。そのためのコードの形式は、テキストの入力に使用したものと同様です。UI コンポーネントが異なるため、
withId()
関数に渡す ID 名が異なります。アプローチの唯一の違いはViewAction
です。typeText()
の代わりにclick()
関数を使用します。
onView(withId(R.id.calculate_button))
.perform(click())
- 最後に、正しいチップ金額が表示されるというアサーションを行う必要があります。想定されるチップ金額は $10.00 です。このテストでは、ID が
tip_result
のTextView
に、想定されるチップ金額が文字列形式で含まれていることを確認します。
onView(withId(R.id.tip_result))
.check(matches(withText(containsString("$10.00"))))
プロンプトが表示されたら、次のインポートを選択します。
import androidx.test.espresso.assertion.ViewAssertions.matches
import org.hamcrest.Matchers.containsString
ここでは、ViewAssertion
を受け取る、check()
という別のインタラクションを使用しました。ViewAssertion
は、UI コンポーネントに使用される特別な Espresso アサーションと考えることができます。TextView
のコンテンツが文字列 "$10.00"
を含むテキストと一致する、というアサーションです。
テストを実行する前に、インポートとコードが正しいことを確認します。次のようになっている必要があります(インポートの順番はこのとおりでなくてもかまいません)。
package com.example.tiptime
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.Matchers.containsString
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class CalculatorTests {
@get:Rule()
val activity = ActivityScenarioRule(MainActivity::class.java)
@Test
fun calculate_20_percent_tip() {
onView(withId(R.id.cost_of_service_edit_text))
.perform(typeText("50.00"))
onView(withId(R.id.calculate_button)).perform(click())
onView(withId(R.id.tip_result))
.check(matches(withText(containsString("$10.00"))))
}
}
エミュレータを使用する場合は、エミュレータと Android Studio のウィンドウを両方同時に表示できることを確認してください。単体テストのときと同じ方法(関数の左側にある赤と緑の矢印ボタンを右クリックして最初の項目を選択)でテストを実行し、結果を確認します。
ユーザーが直接アプリを操作しているかのようにテストが実行されることがわかります。
5. テストスイートを拡張する
これで、初めてのインストルメンテーション テストが完了しました。
さらに挑戦したい場合は、他の割合のチップをテストする関数を追加して、テストスイートを拡張しましょう。その際には、前述した関数と同じ形式を使用します。変更すべき点は、別の割合のチップを選択するコードを記述することと、想定される結果が変わることを考慮して containsString()
に渡す値を変更することのみです。端数切り上げスイッチもあることを忘れてはいけません。端数切り上げスイッチを切り替えるには、onView(withId())
で示したように、ID でコンポーネントを見つけてクリックします。
6. 解答コード
7. まとめ
- Android Studio は、プロジェクトの作成時に必要なテストクラスを生成します。プロジェクトにテストクラスが存在しない場合は、手動で作成できます。
- テストルールは、テストクラス内の各テストの前に実行されます。
- Espresso は、インストルメンテーション テストの基本的なコンポーネントです。UI コンポーネントをコードで操作できます。
長時間の学習、お疲れさまでした。