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" />
此处 @ 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)
答案 0 :(得分:1)
由于您的drawable是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;
}
答案 1 :(得分:1)
要改善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 ((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)))
答案 2 :(得分:0)
这对我有用:HasBackgroundMatcher
onView(allOf(withId(R.id. loginTextView),
hasBackground(R.drawable. sign_in_login_bg), isDisplayed()));