Android. Espresso: can't check background

Related searches

Windows 10 (64 bit), Android Studio 3.1.2, Gradle 4.4, Java 1.8.

Here my layout xml

<TextView
            android:id="@+id/loginTextView"
            android:layout_width="255dp"
            android:layout_height="60dp"
            android:layout_marginBottom="15dp"
            android:background="@drawable/sign_in_login_bg"
            android:gravity="center"                              
            app:layout_constraintBottom_toTopOf="@+id/registerTextView"
            app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

Here @drawable/sign_in_login_bg

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/color_primary" />
    <corners android:radius="@dimen/text_view_rounded_corner_radius" />
</shape>

I want to write espresso test that check that loginTextView has background @drawable/sign_in_login_bg

So I write custom matcher:

import org.hamcrest.Matcher;
      public static Matcher<View> withBackground(final int expectedResourceId) {

            return new BoundedMatcher<View, View>(View.class) {

                @Override
                public boolean matchesSafely(View view) {
                    return sameBitmap(view.getContext(), view.getBackground(), expectedResourceId);
                }

                @Override
                public void describeTo(Description description) {
                    description.appendText("has background resource " + expectedResourceId);
                }
            };
    }

Here method sameBitmap:

private static boolean sameBitmap(Context context, Drawable drawable, int expectedId) {
        Drawable expectedDrawable = ContextCompat.getDrawable(context, expectedId);
        if (drawable == null || expectedDrawable == null) {
            return false;
        }
        if (drawable instanceof StateListDrawable && expectedDrawable instanceof StateListDrawable) {
            drawable = drawable.getCurrent();
            expectedDrawable = expectedDrawable.getCurrent();
        }
        if (drawable instanceof BitmapDrawable) {
            Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
            Bitmap otherBitmap = ((BitmapDrawable) expectedDrawable).getBitmap();
            return bitmap.sameAs(otherBitmap);
        }
        return false;
    }

And here my espresso test:

@Test
    public void loginTextViewBackground() {
        onView(withId(R.id.loginTextView)).check(matches(withBackground(R.drawable.sign_in_login_bg)));
}

But I get error:

android.support.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'has background resource 2131230909' doesn't match the selected view.
Expected: has background resource 2131230909
Got: "AppCompatTextView{id=2131296429, res-name=loginTextView, visibility=VISIBLE, width=765, height=180, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params=android.support.constraint.ConstraintLayout$LayoutParams@bcce82, tag=null, root-is-layout-requested=false, has-input-connection=false, x=158.0, y=1283.0, text=Login, input-type=0, ime-target=false, has-links=false}"

at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:580)
at android.support.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:90)
at android.support.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:52)
at android.support.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:314)
at android.support.test.espresso.ViewInteraction.check(ViewInteraction.java:291)
at com.myproject.android.activity.SignInActivityTest.loginTextViewBackground(SignInActivityTest.java:158)

at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1879)
Caused by: junit.framework.AssertionFailedError: 'has background resource 2131230909' doesn't match the selected view.
Expected: has background resource 2131230909
Got: "AppCompatTextView{id=2131296429, res-name=loginTextView, visibility=VISIBLE, width=765, height=180, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params=android.support.constraint.ConstraintLayout$LayoutParams@bcce82, tag=null, root-is-layout-requested=false, has-input-connection=false, x=158.0, y=1283.0, text=Login, input-type=0, ime-target=false, has-links=false}"

at android.support.test.espresso.matcher.ViewMatchers.assertThat(ViewMatchers.java:526)

As your drawable is a GradientDrawable it must be handled in your Matcher as well. So, your matcher could look like below:

private static boolean sameBitmap(Context context, Drawable drawable, int expectedId) {
    Drawable expectedDrawable = ContextCompat.getDrawable(context, expectedId);
    if (drawable == null || expectedDrawable == null) {
        return false;
    }

    if (drawable instanceof StateListDrawable && expectedDrawable instanceof StateListDrawable) {
        drawable = drawable.getCurrent();
        expectedDrawable = expectedDrawable.getCurrent();
    }
    if (drawable instanceof BitmapDrawable) {
        Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
        Bitmap otherBitmap = ((BitmapDrawable) expectedDrawable).getBitmap();
        return bitmap.sameAs(otherBitmap);
    }

    if (drawable instanceof VectorDrawable ||
            drawable instanceof VectorDrawableCompat ||
            drawable instanceof GradientDrawable) {
        Rect drawableRect = drawable.getBounds();
        Bitmap bitmap = Bitmap.createBitmap(drawableRect.width(), drawableRect.height(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        Bitmap otherBitmap = Bitmap.createBitmap(drawableRect.width(), drawableRect.height(), Bitmap.Config.ARGB_8888);
        Canvas otherCanvas = new Canvas(otherBitmap);
        expectedDrawable.setBounds(0, 0, otherCanvas.getWidth(), otherCanvas.getHeight());
        expectedDrawable.draw(otherCanvas);
        return bitmap.sameAs(otherBitmap);
    }
    return false;
}

Espresso basics, Thus, you will not see methods like getView() and getCurrentActivity() in the Espresso API. You can still safely operate on views by implementing� Thus, you will not see methods like getView() and getCurrentActivity() in the Espresso API. You can still safely operate on views by implementing your own subclasses of ViewAction and ViewAssertion. API components. The main components of Espresso include the following: Espresso – Entry point to interactions with views (via onView() and onData()).

This worked for me: HasBackgroundMatcher

onView(allOf(withId(R.id. loginTextView),
               hasBackground(R.drawable. sign_in_login_bg), isDisplayed()));

Espresso Web, You can use the Espresso-Web API in conjunction with other Espresso APIs to fully interact with web elements inside WebView objects. In Espresso Android, the ViewAssertions can be passed to the Check() method from the ViewInteraction package. Matches assertion is the most commonly used check which verifies the final state of the view with what is expected.

Improving a little bit Anatolii's answer, first we must define the custom matcher:

fun withBackground(expectedResourceId: Int): Matcher<View?>? {
        return object : BoundedMatcher<View?, View>(View::class.java) {
            override fun matchesSafely(view: View): Boolean {
                return sameBitmap(view.context, view.background, expectedResourceId)
            }

            override fun describeTo(description: Description) {
                description.appendText("has background resource $expectedResourceId")
            }
        }
    }

Secondly, private method sameBitmap taking into consideration all the different alternatives and checking both drawables type:

private fun sameBitmap(context: Context, drawable: Drawable, resourceId: Int): Boolean {
        var drawable: Drawable? = drawable
        var expectedDrawable = ContextCompat.getDrawable(context, resourceId)
        if (drawable == null || expectedDrawable == null) {
            return false
        }

        if (drawable is StateListDrawable && expectedDrawable is StateListDrawable) {
            drawable = drawable.getCurrent()
            expectedDrawable = expectedDrawable.getCurrent()
        }
        if (drawable is BitmapDrawable) {
            val bitmap = drawable.bitmap
            val otherBitmap = (expectedDrawable as BitmapDrawable).bitmap
            return bitmap.sameAs(otherBitmap)
        }

        if ((drawable is VectorDrawable && expectedDrawable is VectorDrawable) || (drawable is VectorDrawableCompat && expectedDrawable is VectorDrawableCompat) || (drawable is GradientDrawable && expectedDrawable is GradientDrawable)) {
            return drawableToBitmap(drawable.current)!!.sameAs(drawableToBitmap(expectedDrawable.current))
        }
        return false
    }

And finally drawableToBitmap method:

private fun drawableToBitmap(drawable: Drawable): Bitmap? {
        var bitmap: Bitmap? = null
        if (drawable is BitmapDrawable) {
            if (drawable.bitmap != null) {
                return drawable.bitmap
            }
        }
        bitmap = if (drawable.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) {
            Bitmap.createBitmap(1,
                1,
                Bitmap.Config.ARGB_8888) // Single color bitmap will be created of 1x1 pixel
        } else {
            Bitmap.createBitmap(drawable.intrinsicWidth,
                drawable.intrinsicHeight,
                Bitmap.Config.ARGB_8888)
        }
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, canvas.width, canvas.height)
        drawable.draw(canvas)
        return bitmap
    }

And this is the way to use it:

onView(withId(R.id.on_boarding_initial_circle)).check(matches(withBackground(R.drawable.bg_gray_circle)))

Create UI tests with Espresso Test Recorder, By recording a test scenario, you can record your interactions with a device and add assertions to verify UI elements in particular snapshots of your app. Espresso � As Android Espresso aims to improve productivity and make developers work more efficient, it can also provide very effective improvements in mobile app quality. Tests are easy and quick to build, execution of test scripts is definitely the fastest compared to any other Android test automation framework , and the light-weight API takes care of keeping tests understandable, easy to maintain and tweak.

Espresso setup instructions, Set up your test environment; Add Espresso dependencies; Set the instrumentation If you do not wish to upload this data, you can opt out by including the� You can run your tests in Android Studio or from the command line. In Android Studio. To create a test configuration in Android Studio, complete the following steps: Open Run > Edit Configurations. Add a new Android Tests configuration. Choose a module. Add a specific instrumentation runner: androidx.test.runner.AndroidJUnitRunner

Espresso, Use Espresso to write concise, beautiful, and reliable Android UI tests. While it can be used for black-box testing, Espresso's full power is� androidx.test.espresso.Espresso Entry point to the Espresso framework. Test authors can initiate testing by using one of the on* methods (e.g. onView) or perform top-level user actions (e.g. pressBack).

The problem is that your application performs the network operation after you click login button. Espresso doesn't handle (wait) network calls to finish by default.

Comments
  • Where's your sameBitmap method defined?
  • I update my post.