Android のコンポーネント(Activity)の単体テスト2 - 別アクティビティの起動
一応、前回の続きです。
pickles-ochazuke.hatenablog.com
概要
今回は、テスト対象のアクティビティから別のアクティビティを起動するテストを作成します。
別のアクティビティが起動したかをテストするのですが、単体テストなので別のアクティビティに強く依存したくはありません。例えば、別のアクティビティを起動して、そのビューが期待通りになっているかどうかまでは確認する必要はないと思います。これは、そのアクティビティ(別のアクティビティ)側の単体テストの役割だからです。
アクティビティを起動する場合は、インテントを作成し、startActivity()
メソッドにインテントを渡すことで Android 側が処理してくれます。ですので、startActivity()
メソッドに期待するインテントが渡されていれば起動できていることにしても良さそうです。
単体テストでは、実機を使うわけではないので、Android のシステムのようにインテントを管理してくれる代わりが必要になります。これは、Espresso
では、Intents
クラスがしてくれます。Espresso のドキュメントでは用語が紛らわしいためか、Espresso-Intents
と表現しています。
この Intents クラスを初期化していると、startActivity() メソッドに渡されたインテントが Intents クラスに記録されます。その後、Intents のインテント検証用のメソッド(intended()
メソッド)を使うことで期待する値になっているか検証できます。
例えば、次のように使います。
Intents.intended(hasComponent(OtherActivity::class.java.name))
intended() メソッドに渡した条件(上記だと OtherActivity クラスの名前を持ったインテントがあるかどうか)を元に Intents が記録しているインテントを検証します。検証した結果、一致したのが一つのときテストが成功します。複数のインテントが一致した場合や、一つも一致しない場合は例外が投げられテストが失敗します。
実践
プロジェクトの準備
ここからは、実際にテストをしてみます。プロジェクトは、前回のプロジェクトをそのまま使います。
前回の最後の状態のプロジェクトは、Github に上げています。ブランチは、ActivityUnitTestExample_1
です。
別のアクティビティ起動の実装
まずは、別のアクティビティを起動する処理を実装します。ここでは、ボタンを押したらアクティビティが起動するようにしたいと思います。
別のアクティビティを起動するためには、そのアクティビティが必要になので、OtherActivity
という名前で作成しておきます。中身は一切編集しないので、テンプレートは何でもいいですが、ここでは EmptyActivity
にしました。
次にボタンを追加します。activity_main.xml
にボタンを追加し、id を button
とします。
レイアウトにボタンを追加したら MainActivity.kt
にボタンの処理を実装します。
import android.widget.Button import android.content.Intent ... class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val button = findViewById<Button>(R.id.button) button.setOnClickListener { val intent = Intent(this, OtherActivity::class.java) startActivityForResult(intent, 1) } } ...
実装に不安があれば、ここで一度エミュレータや実機で確認しましょう。
テストの追加
次のようにテストを追加します。
import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent ... @Test fun ボタンを押すとOtherActivityが起動するべき() { onView(withId(R.id.button)).perform(click()) intended(hasComponent(OtherActivity::class.java.name)) }
Espresso-Intents は拡張機能のため、app/build.gradle
に以下を追加します。
dependencies { ... testImplementation 'androidx.test.espresso:espresso-intents:3.2.0' ... }
Gradle の同期を行い、テストを実行すると java.lang.NullPointerException
が発生すると思います。これは、Intents の初期化がされていないのに intended() メソッドを呼んだためです。Intents.init()
メソッドを呼ぶと初期化されますが、テストの終了時に Intents.release()
を呼ぶ必要があります。
前回、Activity を管理してくれる ActivityScenarioRule
というクラスを使いました。これと似たようなものが Intents 用に存在します。IntentsTestRule
です。
activityScenarioRule()
が書かれた箇所を次のように置き換えます。
import androidx.test.espresso.intent.rule.IntentsTestRule @get:Rule val rule = IntentsTestRule(MainActivity::class.java)
activityScenarioRule
を残していると、アクティビティが二回作成され、テストが上手くいきません。原因は分かっていませんが、おそらく onView() で処理しているアクティビティの対象が activityScenarioRule の方を見ており、その場合、インテントが記録されないためです。
上記を追加したあと、テストを実行すると成功するはずです。
以上です。
ここまでの状態を Github のほうに上げました。ブランチを ActivityUnitTestExample_2
に変更するとその状態になります。
続きを書きました。
pickles-ochazuke.hatenablog.com
雑記
Kotlin のコードが上手く色づけされないのって何でですかね(対応しているらしいですが)。書き方が間違っているのか、何か設定しないといけないのか。全体的に見た目が気になりだしたのてこれを気にいろいろ弄ってみてもいいのかもしれない。