可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm new to Espresso testing, but there doesn't seem like there's any way to test drawable changes.
I have a tutorial that is an ImageView
Drawable
slideshow 'tucked into' a semi-transparent TextView
. In my tests, I want to ensure that when the next button is pressed, the proper Drawable
has been inserted into the tutorial's ImageView
.
There is no default Matcher
to check for Drawable
s, so I set out to write my own using https://stackoverflow.com/a/28785178/981242. Unfortunately, since there is no way to retrieve the id of an ImageView
's active Drawable
, I can't complete the matchesSafely()
implementation.
This can't be the only use case for testing active Drawable
s. What is the tool that people normally use for situations like this?
回答1:
I prefer not to compare bitmaps and instead follow this answer's advice: https://stackoverflow.com/a/14474954/1396068
When setting the image view's drawable, also store the drawable ID in its tag with setTag(R.drawable.your_drawable)
. Then use Espresso's withTagValue(equalTo(R.drawable.your_drawable))
matchers to check for the correct tag.
回答2:
please check this tutorial I found.
Seems to work pretty good https://medium.com/@dbottillo/android-ui-test-espresso-matcher-for-imageview-1a28c832626f#.4snjg8frw
Here is the summary for copy pasta ;-)
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("]");
}
}
}
Please be aware that this only works when your Drawable
is a BitmapDrawable
. If you also have VectorDrawable
or other Drawable
you have to check for this (imageView.getDrawable() instanceOf XXXDrawable
) and get the bitmap out of it. Except you have some kind of simple Drawable where you just have one color or so you can compare.
To get the Bitmap of a VectorDrawable for example you have to draw the VectorDrawable to a canvas and save it to a bitmap (I had some trouble when the VectorDrawable was tinted).
If you have a StateListDrawable you can get the Drawable of the selected state and repeat your if instanceOf cascade.
For other Drawable types I don't have any experience, sorry!
回答3:
There is one gist which contains withBackground()
, withCompoundDrawable()
, withImageDrawable()
matchers from Frankie Sardo. All credits to him.
And regarding image ids - you can type R.drawable.image_name
, then the id of the drawable will be retrieved automatically.
回答4:
Based on @wolle and @FreewheelNat's help, for comparing (Vector) 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("]");
}
}
}
回答5:
I accept the answer of @wolle as valid, but I would like to admit that, even for Java, it could be even simpler than that. It can be converted into a static function
(or a companion
in Kotlin) and also clean some deprecated code.
Anyway, the code-compacted-non-deprecated solution for Kotlin would be this:
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 lines vs 44, eh?
回答6:
I already answered on the similar topic here: Get the ID of a drawable in ImageView.
The approach is based on tagging a view with a specified resource id in the custom LayoutInflater
. Whole process is automated by a simple library 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 section.
As a result, you can compare two drawables just by their ids:
onView(withId(R.id.imageview)).check(assertTagKeyValue(
ViewTag.IMAGEVIEW_SRC.id, android.R.drawable.ic_media_play));
Custom Espresso assertion assertTagKeyValue
is available here