unit-testingkotlingradientdrawable

GetColor from GradientDrawable in kotlin


I've programmatically set a GradientDrawable background up like so, and am trying to test for it's color. The main issue seems to be getting the color from the shape (GradientDrawable) at test time.

This snippet is from within a larger binding adapter..

color = ContextCompat.getColor(
    textView.context, R.color.ColorRatingHigh
)

val shape = GradientDrawable()
shape.shape = GradientDrawable.OVAL
shape.setColor(color)
shape.setStroke(2, Color.BLACK)
textView.setBackground(shape)

The test is set up like so..

@Test
fun MovieDetailRatingColorTest() {
    ...

    onView(withId(R.id.movie_vote_average))
        .check(matches(withBackgroundColor(R.color.ColorRatingHigh)))
}
...

fun withBackgroundColor(expectedColor: Int): Matcher<View?>? {
    Checks.checkNotNull(expectedColor)
    return object : BoundedMatcher<View?, TextView>(TextView::class.java) {
        override fun matchesSafely(textView: TextView): Boolean {

            val actualColor = (textView.getBackground() as ColorDrawable).color
            return expectedColor == actualColor
        }

        override fun describeTo(description: Description) {
            description.appendText("with background color: ")
        }
    }
}

unfortunately I'm getting the following ClassCastException

android.graphics.drawable.GradientDrawable cannot be cast to android.graphics.drawable.ColorDrawable

I've seen a few post on the site regarding similar issues to this,

but none seem to be working and most end up with the same issues.. eg Testing background color espresso Android

or have answers which seem outdated or suffer from java to kotlin conversion.. eg how to get the color from GradientDrawable


Solution

  • came up with a workaround, using the fragment scenario to get access to 'ContextCompat'.

    This allowed me to retrieve the 'R.color' directory. getColor retrieves the 2's complement of the colorId that was originally being passed in... which happens to match the id retrieved here: val actualColor = (textView.getBackground() as ColorDrawable).color.defaultColor

    lateinit var scenario: FragmentScenario<MovieDetailFragment>
    ...
    
    @Test
    fun MovieDetailRatingColorTest() {
        var expectedColor: Int? = 0
    
        scenario.onFragment { fragment ->
                expectedColor =
                    fragment.context?.let {
                        ContextCompat.getColor(it, R.color.ColorRatingHigh )
                    }
        }
    
        onView(withId(R.id.movie_vote_average))
            .check(matches(withBackgroundColor(expectedColor)))
    }
    

    then I editted the withBackground() function to match the new input

    fun withBackgroundColor(expectedColor: Int?): Matcher<View?>? {
        Checks.checkNotNull(expectedColor)
        return object : BoundedMatcher<View?, TextView>(TextView::class.java) {
            override fun matchesSafely(textView: TextView): Boolean {
    
                val actualColor = (textView.getBackground() as GradientDrawable).color?.defaultColor
                return expectedColor == actualColor
            }
    
            override fun describeTo(description: Description) {
                description.appendText("with background color: $expectedColor")
            }
        }
    }