安卓。Espresso:无法检查背景

3

操作系统为Windows 10(64位),使用Android Studio 3.1.2,Gradle版本为4.4,Java版本为1.8。

以下是我的布局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>

我希望编写一个espresso测试,检查loginTextView是否具有背景@drawable/sign_in_login_bg

因此,我编写了自定义匹配器:

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);
        }
    };
}

这里是方法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;
}

以下是我的浓缩咖啡测试结果:

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

但是我遇到了错误:
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)

你的sameBitmap方法定义在哪里? - Anatolii
我更新了我的帖子。 - Alexei
3个回答

8

由于您的可绘制对象是GradientDrawable,因此您的Matcher也必须进行处理。因此,您的匹配器可能如下所示:

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;
}

2
改进一下Anatolii的答案,首先我们必须定义自定义匹配器:
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")
            }
        }
    }

其次,私有方法 sameBitmap 考虑了所有不同的替代方案,并检查两个可绘制对象的类型:
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(currentDrawable is ColorDrawable && expectedDrawable is ColorDrawable) {
            val expectedColor = ContextCompat.getColor(context, resourceId)
            return currentDrawable.color == expectedColor
        }

        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
    }

最后是drawableToBitmap方法:

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
    }

这是使用它的方法:
onView(withId(R.id.on_boarding_initial_circle)).check(matches(withBackground(R.drawable.bg_gray_circle)))
onView(withId(R.id.on_boarding_initial_circle)).check(matches(withBackground(R.color.color_primary)))

1
我用了这个方法:HasBackgroundMatcher,效果很好。
onView(allOf(withId(R.id. loginTextView),
               hasBackground(R.drawable. sign_in_login_bg), isDisplayed()));

这无法检查带有颜色资源的背景,与我编辑以使其成为可能的@carlos-daniel答案相反。 - Rubén Viguera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接