AndroidでAlertDialogを使っている場合のテスト
AndroidでEditTextの入力値チェックで不正だった場合、AlertDialogを使う場合があります。他、何かとユーザに注意喚起させたい場合に使うことがあると思います。
Fragment方式(Activity1個に対してFragment複数で画面遷移する方式)でFragmentをinstrumentテストすると、AlertDialogを使っているとうまく動きません。何もせずに実行するとAlertDialogが出たところで・・・
androidx.test.espresso.PerformException: Error performing 'single click - At Coordinates: 600, 1319 and precision: 16, 16' on view 'view.getId() is <2131296376/jp.co.suzuken.ocrexpirchk:id/clearBtn>'.
at androidx.test.espresso.PerformException$Builder.build(PerformException.java:1)
at androidx.test.espresso.base.PerformExceptionHandler.handleSafely(PerformExceptionHandler.java:8)
at androidx.test.espresso.base.PerformExceptionHandler.handleSafely(PerformExceptionHandler.java:9)
at androidx.test.espresso.base.DefaultFailureHandler$TypedFailureHandler.handle(DefaultFailureHandler.java:4)
at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:5)
at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:8)
at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:11)
at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:8)
at jp.co.suzuken.ocrexpirchk.main.OcrFragmentInstTest.clearTest01(OcrFragmentInstTest.kt:400)
... 34 trimmed
Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
Theme.AppCompat(か、それを継承したテーマ)を使えと言ってきます。
これは、おそらくFragmentScenarioの仕様で、実機、エミュレータで実行する場合と違ってFragmentとその親であるActivityの関係が異なるからだと思います。(後述)
ActivityからAlertDialogを表示する場合はこのような事象は起こりません。
これは、launchFragmentInContainerの引数でテーマを渡すことによって解決します。
launchFragmentInContainer<MyFragment>(themeResId = androidx.appcompat.R.style.Theme_AppCompat).apply { // ★
onFragment { fragment ->
assertThat(fragment).isNotNull()
}
}
onView(withId(R.id.clearBtn)).perform(click())
onView(withText("全データの削除"))
.inRoot(isDialog()) // ★ AlertDialogの場合、これ必要
.check(matches(isDisplayed()))
onView(withText("データを削除しますか?"))
.inRoot(isDialog()) // ★ AlertDialogの場合、これ必要
.check(matches(isDisplayed()))
onView(withText("はい"))
.inRoot(isDialog()) // ★ AlertDialogの場合、これ必要
.check(matches(isDisplayed()))
.perform(click())
AlertDialogのGUII上の部品をassertするためには、
.inRoot(isDialog())
が必要になります。
requireActivity()、requireContext()
requireActivity()、requireContext()の使い方も注意が必要です。requireActivity()、requireContext()自体はinstrumentテストでもエラーになることはありませんが、
requireActivity() as MainActivity
はClassCasstExceptionになります。
java.lang.ClassCastException: androidx.fragment.app.testing.EmptyFragmentActivity cannot be cast to jp.co.hogehge.myapp.MainActivity
これもFragmentScenarioの仕様なのか、Fragmentの親ActivityはガチのMainActivityではなく、androidx.fragment.app.testing.EmptyFragmentActivity
を渡してきます。これは、
if (requireActivity() is MainActivity) {
val myActivity = requireActivity() as MainActivity
・・・
}
のようにif文で囲うことによりinstrumentテストではバイパスはできますが、このif文のtrueの部分をinstrumentテストしたいときは困ったことになります。
launchFragmentInContainerの引数のFragmentFactoryをうまいこと使えばできるのかもしれませんが・・・今のところ解決策はありません。
FragmentScenarioのFragmentの親Activityの件について何か情報をお持ちの方がいらっしゃったらコメントお願いします。