Android EspressoのCustom Matcher実装方法

カスタムマッチ

前回の記事AndroidX Espresso Matcher分析でMatcherがどのように動作するか、コード分析をしました。 今回はCustom Matcherを直接実装してみます。理解を助けるためにTextViewのtextを比較するCustom Matcherを実装してみます。 ViewMatchers.withText()と同じ機能をします。

デフォルトのプロジェクトのコードは、GitHubからダウンロードしてください。 Android EspressoにUIテスト(1)文で使用したサンプルコードです。

実装

androidTestフォルダにCustomMatcher.ktを生成し、次のコードを入力してください。

withText()の引数としてStringまたはResourceIdが配信されることがありますので、 二つをそれぞれ異なる方法で処理することができるようにTextStringMatcherとTextResourceMatcherクラスを生成しました。 Stringの場合 Matcher<String>に変換して、既に実装されてロジックで処理し、ResourceIdの場合Stringを持ってきて、直接equals()で比較するように実装した。

object CustomMatcher {

    fun withText(substring: String): Matcher<View> {
        return withText(`is`(substring))
    }

    fun withText(resourceId: Int): Matcher<View> {
        return TextResourceMatcher(resourceId)
    }

    private fun withText(stringMatcher: Matcher<String>): Matcher<View> {
        checkNotNull(stringMatcher)
        return TextStringMatcher(stringMatcher)
    }

    private class TextStringMatcher(private val stringMatcher: Matcher<String>)
            : BoundedMatcher<View, TextView>(TextView::class.java) {
        public override fun matchesSafely(view: TextView): Boolean {
            val text: String = view.text.toString()
            return text != null && stringMatcher.matches(text)
        }

        override fun describeTo(description: Description) {
            description.appendText("with text: ")
            stringMatcher.describeTo(description)
        }
    }

    private class TextResourceMatcher(private val resourceId: Int)
            : BoundedMatcher<View, TextView>(TextView::class.java) {
        private var expectedText: String? = null

        public override fun matchesSafely(view: TextView): Boolean {
            expectedText = view.resources.getString(resourceId)
            val actualText: String = view.text.toString()
            return actualText != null && actualText.equals(expectedText)
        }

        override fun describeTo(description: Description) {
            if (expectedText != null) {
                description.appendText("with text: $expectedText")
            }
        }
    }
}

Custom Matcherがよく作成されたテストコードを作成してみましょう。 次のコードのように、最初に引数としてStringを直接転送し、その次には、ResourceIdを引数として伝えました。

テストを実行してみると、すべてのパスしました。 細かいバグがあるか分からないが、 ViewMatchers.withText()と同じ機能をするCustom Matcherが完成しました!

@RunWith(AndroidJUnit4::class)
@LargeTest
class CustomMatcherTest {
    @Rule
    @JvmField
    var activityRule = ActivityTestRule(LocaleActivity::class.java)

    @Test
    fun noCountryExtra() {
        activityRule.launchActivity(Intent())
        Espresso.onView(withId(R.id.tvLocale))
            .check(matches(CustomMatcher.withText("No country string")))

        Espresso.onView(withId(R.id.tvLocale))
            .check(matches(CustomMatcher.withText(R.string.no_country_extra)))
    }
}

まとめ

AndroidX Espresso Matcher分析でMatcherのコードを分析し見て、今回はCustom Matcherを作成しました。 基本的なMatcherは既に作られているので、追加で作成があまりないようです。エスプレッソの内部構造を理解するのに役に立てば幸いです。

参考

Related Posts

codechachaCopyright ©2019 codechacha