使用 Espresso 测试可绘制更改
Using Espresso to test drawable changes
我是 Espresso 测试的新手,但似乎没有任何方法可以测试可绘制的更改。
我有一个教程是 ImageView
Drawable
幻灯片 'tucked into' 半透明 TextView
。在我的测试中,我想确保在按下下一个按钮时,正确的 Drawable
已插入教程的 ImageView
.
没有默认的 Matcher
来检查 Drawable
,所以我开始使用 编写自己的代码。不幸的是,由于无法检索 ImageView
的活动 Drawable
的 ID,我无法完成 matchesSafely()
实施。
这不能是测试活动 Drawable
的唯一用例。人们通常在这种情况下使用什么工具?
请查看我找到的这个教程。
好像还不错https://medium.com/@dbottillo/android-ui-test-espresso-matcher-for-imageview-1a28c832626f#.4snjg8frw
这是复制意大利面的摘要 ;-)
public class DrawableMatcher extends TypeSafeMatcher<View> {
private final int expectedId;
String resourceName;
public DrawableMatcher(int expectedId) {
super(View.class);
this.expectedId = expectedId;
}
@Override
protected boolean matchesSafely(View target) {
if (!(target instanceof ImageView)){
return false;
}
ImageView imageView = (ImageView) target;
if (expectedId < 0){
return imageView.getDrawable() == null;
}
Resources resources = target.getContext().getResources();
Drawable expectedDrawable = resources.getDrawable(expectedId);
resourceName = resources.getResourceEntryName(expectedId);
if (expectedDrawable == null) {
return false;
}
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap otherBitmap = ((BitmapDrawable) expectedDrawable).getBitmap();
return bitmap.sameAs(otherBitmap);
}
@Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(expectedId);
if (resourceName != null) {
description.appendText("[");
description.appendText(resourceName);
description.appendText("]");
}
}
}
请注意,这仅在您的 Drawable
是 BitmapDrawable
时有效。如果您还有 VectorDrawable
或其他 Drawable
,您必须检查它 (imageView.getDrawable() instanceOf XXXDrawable
) 并从中获取位图。除了你有某种简单的 Drawable,你只有一种颜色,所以你可以比较。
例如,要获取 VectorDrawable 的位图,您必须将 VectorDrawable 绘制到 canvas 并将其保存到位图(我在为 VectorDrawable 着色时遇到了一些麻烦)。
如果您有一个 StateListDrawable,您可以获得所选状态的 Drawable 并重复您的 if instanceOf 级联。
其他的Drawable类型我没有任何经验,抱歉!
有一个要点包含 withBackground()
、withCompoundDrawable()
、withImageDrawable()
个来自 Frankie Sardo 的匹配器。所有功劳都归功于他。
关于图像 ID - 您可以键入 R.drawable.image_name
,然后将自动检索可绘制对象的 ID。
基于@wolle 和@FreewheelNat 的帮助,用于比较(矢量)Drawable:
public static Matcher<View> withDrawableId(@DrawableRes final int id) {
return new DrawableMatcher(id);
}
public static class DrawableMatcher extends TypeSafeMatcher<View> {
private final int expectedId;
private String resourceName;
public DrawableMatcher(@DrawableRes int expectedId) {
super(View.class);
this.expectedId = expectedId;
}
@Override
protected boolean matchesSafely(View target) {
if (!(target instanceof ImageView)) {
return false;
}
ImageView imageView = (ImageView) target;
if (expectedId < 0) {
return imageView.getDrawable() == null;
}
Resources resources = target.getContext().getResources();
Drawable expectedDrawable = resources.getDrawable(expectedId);
resourceName = resources.getResourceEntryName(expectedId);
if (expectedDrawable != null && expectedDrawable.getConstantState() != null) {
return expectedDrawable.getConstantState().equals(
imageView.getDrawable().getConstantState()
);
} else {
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(expectedId);
if (resourceName != null) {
description.appendText("[");
description.appendText(resourceName);
description.appendText("]");
}
}
}
我不想比较位图,而是遵循这个答案的建议:
设置图像视图的可绘制对象时,还要将可绘制对象 ID 存储在其带有 setTag(R.drawable.your_drawable)
的标记中。然后使用 Espresso 的 withTagValue(equalTo(R.drawable.your_drawable))
匹配器来检查正确的标签。
我接受@wolle 的回答是有效的,但我想承认,即使对于 Java,它甚至可能比 更简单 。它可以转换为 static function
(或 Kotlin 中的 companion
),还可以清理一些 弃用的代码 。
无论如何,Kotlin 的代码压缩但不弃用的解决方案是这样的:
fun drawableIsCorrect(@DrawableRes drawableResId: Int): Matcher<View> {
return object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("with drawable from resource id: ")
description.appendValue(drawableResId)
}
override fun matchesSafely(target: View?): Boolean {
if (target !is ImageView) {
return false
}
if (drawableResId < 0) {
return target.drawable == null
}
val expectedDrawable = ContextCompat.getDrawable(target.context, drawableResId)
?: return false
val bitmap = (target.drawable as BitmapDrawable).bitmap
val otherBitmap = (expectedDrawable as BitmapDrawable).bitmap
return bitmap.sameAs(otherBitmap)
}
}
}
22 行对 44 行,嗯?
我已经在这里回答过类似的话题:Get the ID of a drawable in ImageView。
该方法基于在自定义 LayoutInflater
中标记具有指定资源 ID 的视图。整个过程由一个简单的库 TagView. It's especially handy for Espresso test because you don't need to tag every view in your project manually. In fact, you don't need to change anything, except you set some drawables in runtime. In that case you need to look into Tagging in runtime 部分自动完成。
因此,您可以仅通过它们的 id 来比较两个可绘制对象:
onView(withId(R.id.imageview)).check(assertTagKeyValue(
ViewTag.IMAGEVIEW_SRC.id, android.R.drawable.ic_media_play));
自定义 Espresso 断言 assertTagKeyValue
可用 here
这是@drakeet 回答的 Kotlin 版本,稍作修改。
class DrawableMatcher(private val targetContext: Context,
@param:DrawableRes private val expectedId: Int) : TypeSafeMatcher<View>(View::class.java) {
override fun matchesSafely(target: View): Boolean {
val drawable: Drawable? = when(target) {
is ActionMenuItemView -> target.itemData.icon
is ImageView -> target.drawable
else -> null
}
requireNotNull(drawable)
val resources: Resources = target.context.resources
val expectedDrawable: Drawable? = resources.getDrawable(expectedId, targetContext.theme)
return expectedDrawable?.constantState?.let { it == drawable.constantState } ?: false
}
override fun describeTo(description: Description) {
description.appendText("with drawable from resource id: $expectedId")
targetContext.resources.getResourceEntryName(expectedId)?.let { description.appendText("[$it]") }
}
}
我是 Espresso 测试的新手,但似乎没有任何方法可以测试可绘制的更改。
我有一个教程是 ImageView
Drawable
幻灯片 'tucked into' 半透明 TextView
。在我的测试中,我想确保在按下下一个按钮时,正确的 Drawable
已插入教程的 ImageView
.
没有默认的 Matcher
来检查 Drawable
,所以我开始使用 ImageView
的活动 Drawable
的 ID,我无法完成 matchesSafely()
实施。
这不能是测试活动 Drawable
的唯一用例。人们通常在这种情况下使用什么工具?
请查看我找到的这个教程。 好像还不错https://medium.com/@dbottillo/android-ui-test-espresso-matcher-for-imageview-1a28c832626f#.4snjg8frw
这是复制意大利面的摘要 ;-)
public class DrawableMatcher extends TypeSafeMatcher<View> {
private final int expectedId;
String resourceName;
public DrawableMatcher(int expectedId) {
super(View.class);
this.expectedId = expectedId;
}
@Override
protected boolean matchesSafely(View target) {
if (!(target instanceof ImageView)){
return false;
}
ImageView imageView = (ImageView) target;
if (expectedId < 0){
return imageView.getDrawable() == null;
}
Resources resources = target.getContext().getResources();
Drawable expectedDrawable = resources.getDrawable(expectedId);
resourceName = resources.getResourceEntryName(expectedId);
if (expectedDrawable == null) {
return false;
}
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
Bitmap otherBitmap = ((BitmapDrawable) expectedDrawable).getBitmap();
return bitmap.sameAs(otherBitmap);
}
@Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(expectedId);
if (resourceName != null) {
description.appendText("[");
description.appendText(resourceName);
description.appendText("]");
}
}
}
请注意,这仅在您的 Drawable
是 BitmapDrawable
时有效。如果您还有 VectorDrawable
或其他 Drawable
,您必须检查它 (imageView.getDrawable() instanceOf XXXDrawable
) 并从中获取位图。除了你有某种简单的 Drawable,你只有一种颜色,所以你可以比较。
例如,要获取 VectorDrawable 的位图,您必须将 VectorDrawable 绘制到 canvas 并将其保存到位图(我在为 VectorDrawable 着色时遇到了一些麻烦)。 如果您有一个 StateListDrawable,您可以获得所选状态的 Drawable 并重复您的 if instanceOf 级联。 其他的Drawable类型我没有任何经验,抱歉!
有一个要点包含 withBackground()
、withCompoundDrawable()
、withImageDrawable()
个来自 Frankie Sardo 的匹配器。所有功劳都归功于他。
关于图像 ID - 您可以键入 R.drawable.image_name
,然后将自动检索可绘制对象的 ID。
基于@wolle 和@FreewheelNat 的帮助,用于比较(矢量)Drawable:
public static Matcher<View> withDrawableId(@DrawableRes final int id) {
return new DrawableMatcher(id);
}
public static class DrawableMatcher extends TypeSafeMatcher<View> {
private final int expectedId;
private String resourceName;
public DrawableMatcher(@DrawableRes int expectedId) {
super(View.class);
this.expectedId = expectedId;
}
@Override
protected boolean matchesSafely(View target) {
if (!(target instanceof ImageView)) {
return false;
}
ImageView imageView = (ImageView) target;
if (expectedId < 0) {
return imageView.getDrawable() == null;
}
Resources resources = target.getContext().getResources();
Drawable expectedDrawable = resources.getDrawable(expectedId);
resourceName = resources.getResourceEntryName(expectedId);
if (expectedDrawable != null && expectedDrawable.getConstantState() != null) {
return expectedDrawable.getConstantState().equals(
imageView.getDrawable().getConstantState()
);
} else {
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(expectedId);
if (resourceName != null) {
description.appendText("[");
description.appendText(resourceName);
description.appendText("]");
}
}
}
我不想比较位图,而是遵循这个答案的建议:
设置图像视图的可绘制对象时,还要将可绘制对象 ID 存储在其带有 setTag(R.drawable.your_drawable)
的标记中。然后使用 Espresso 的 withTagValue(equalTo(R.drawable.your_drawable))
匹配器来检查正确的标签。
我接受@wolle 的回答是有效的,但我想承认,即使对于 Java,它甚至可能比 更简单 。它可以转换为 static function
(或 Kotlin 中的 companion
),还可以清理一些 弃用的代码 。
无论如何,Kotlin 的代码压缩但不弃用的解决方案是这样的:
fun drawableIsCorrect(@DrawableRes drawableResId: Int): Matcher<View> {
return object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("with drawable from resource id: ")
description.appendValue(drawableResId)
}
override fun matchesSafely(target: View?): Boolean {
if (target !is ImageView) {
return false
}
if (drawableResId < 0) {
return target.drawable == null
}
val expectedDrawable = ContextCompat.getDrawable(target.context, drawableResId)
?: return false
val bitmap = (target.drawable as BitmapDrawable).bitmap
val otherBitmap = (expectedDrawable as BitmapDrawable).bitmap
return bitmap.sameAs(otherBitmap)
}
}
}
22 行对 44 行,嗯?
我已经在这里回答过类似的话题:Get the ID of a drawable in ImageView。
该方法基于在自定义 LayoutInflater
中标记具有指定资源 ID 的视图。整个过程由一个简单的库 TagView. It's especially handy for Espresso test because you don't need to tag every view in your project manually. In fact, you don't need to change anything, except you set some drawables in runtime. In that case you need to look into Tagging in runtime 部分自动完成。
因此,您可以仅通过它们的 id 来比较两个可绘制对象:
onView(withId(R.id.imageview)).check(assertTagKeyValue(
ViewTag.IMAGEVIEW_SRC.id, android.R.drawable.ic_media_play));
自定义 Espresso 断言 assertTagKeyValue
可用 here
这是@drakeet 回答的 Kotlin 版本,稍作修改。
class DrawableMatcher(private val targetContext: Context,
@param:DrawableRes private val expectedId: Int) : TypeSafeMatcher<View>(View::class.java) {
override fun matchesSafely(target: View): Boolean {
val drawable: Drawable? = when(target) {
is ActionMenuItemView -> target.itemData.icon
is ImageView -> target.drawable
else -> null
}
requireNotNull(drawable)
val resources: Resources = target.context.resources
val expectedDrawable: Drawable? = resources.getDrawable(expectedId, targetContext.theme)
return expectedDrawable?.constantState?.let { it == drawable.constantState } ?: false
}
override fun describeTo(description: Description) {
description.appendText("with drawable from resource id: $expectedId")
targetContext.resources.getResourceEntryName(expectedId)?.let { description.appendText("[$it]") }
}
}