Espresso与RecyclerView内部的可绘制对象不匹配

时间:2018-10-25 14:01:50

标签: android android-espresso

我正在尝试检查RecyclerView中的项目是否包含预期的图像可绘制对象。我尝试检查后代,但在获取图像视图本身的ID而不是我要查找的可绘制对象时遇到错误。

onView(new RecyclerViewMatcher(R.id.rv_sub_services).atPositionOnView(0, R.id.iv_service_image))
        .check(matches(atPosition(0, hasDescendant(withId(R.drawable.eap_financial)))));

Expected: has item at position 0: has descendant: with id: 2131165345
Got: "AppCompatImageView{id=2131296664, res-name=iv_service_image, desc=Resource 
type, visibility=VISIBLE, width=36...}

我也尝试过使用自定义匹配器,但这让我更加沮丧,因为当我尝试将文本与textview进行匹配时,效果很好,但是当我尝试将imageview与R.drawable id进行匹配时,效果却不佳。

我正在谈论的那个人:

public class RecyclerViewMatcher {
    private final int recyclerViewId;

    public RecyclerViewMatcher(int recyclerViewId) {
        this.recyclerViewId = recyclerViewId;
    }

    public Matcher<View> atPosition(final int position) {
        return atPositionOnView(position, -1);
    }

    public Matcher<View> atPositionOnView(final int position, final int targetViewId) {

        return new TypeSafeMatcher<View>() {
            View childView;

            public void describeTo(Description description) {

            }

            public boolean matchesSafely(View view) {

                if (childView == null) {
                    RecyclerView recyclerView = view.getRootView().findViewById(recyclerViewId);
                    if (recyclerView != null && recyclerView.getId() == recyclerViewId) {
                        childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
                    } else {
                        return false;
                    }
                }

                if (targetViewId == -1) {
                    return view == childView;
                } else {
                    View targetView = childView.findViewById(targetViewId);
                    return view == targetView;
                }

            }
        };
    }
}

这段代码适用于textview

onView(new RecyclerViewMatcher(R.id.rv_sub_services).atPositionOnView(0, R.id.tv_title)).check(matches(withText("Financial Assistance")));

但不适用于imageview:

onView(new RecyclerViewMatcher(R.id.rv_sub_services).atPositionOnView(0, R.id.iv_service_image)).check(matches(withId(R.drawable.eap_financial));

非常感谢您的帮助。 非常感谢。

更新

添加匹配器,以在不是一个recyclerView项时比较可绘制对象,以防也可能适合此方案?

public class EspressoTestsMatchers {

    public Matcher<View> withDrawable(final int resourceId) {
        return new DrawableMatcher(resourceId);
    }

    public Matcher<View> noDrawable() {
        return new DrawableMatcher(-1);
    }
}

public class DrawableMatcher extends TypeSafeMatcher<View> {
    private final int expectedId;

    public DrawableMatcher(int resourceId) {
        super(View.class);
        this.expectedId = resourceId;
    }

    @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);
        if (expectedDrawable == null) {
            return false;
        }
        Bitmap bitmap = getBitmap(imageView.getDrawable());
        Bitmap otherBitmap = getBitmap(expectedDrawable);
        return bitmap.sameAs(otherBitmap);
    }

    private Bitmap getBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    @Override
    public void describeTo(Description description) {

    }
}

1 个答案:

答案 0 :(得分:1)

如果您的匹配者必须在setBounds上调用View,我认为这是不安全,因为测试结果可能会意外更改。您可以尝试在Espresso中基于ImageViewHasBackgroundMatcher创建自定义匹配器:

public static Matcher<View> hasImage(int drawableId) {
    return new BoundedMatcher<View, ImageView>(ImageView.class) {

        @Override public void describeTo(Description description) {
            description.appendText("has image with drawable ID: " + drawableId);
        }

        @Override protected boolean matchesSafely(ImageView view) {
            return assertDrawable(view.getDrawable(), drawableId, view);
        }
    };
}

private static boolean compareBitmaps(Bitmap img1, Bitmap img2) {
    if (img1.getWidth() == img2.getWidth() && img1.getHeight() == img2.getHeight()) {
        int[] img1Pixels = new int[img1.getWidth() * img1.getHeight()];
        int[] img2Pixels = new int[img2.getWidth() * img2.getHeight()];

        img1.getPixels(img1Pixels, 0, img1.getWidth(), 0, 0, img1.getWidth(), img1.getHeight());
        img2.getPixels(img2Pixels, 0, img2.getWidth(), 0, 0, img2.getWidth(), img2.getHeight());

        return Arrays.equals(img1Pixels, img2Pixels);
    }
    return false;
}

private static boolean assertDrawable(Drawable actual, int expectedId, View v) {
    if (!(actual instanceof BitmapDrawable)) {
        return false;
    }

    Bitmap expectedBitmap = null;
    try {
        expectedBitmap = BitmapFactory.decodeResource(v.getContext().getResources(), expectedId);
        if (Build.VERSION.SDK_INT >= 12) {
            return ((BitmapDrawable) actual).getBitmap().sameAs(expectedBitmap);
        } else {
            return compareBitmaps(((BitmapDrawable) actual).getBitmap(), expectedBitmap);
        }
    } catch (OutOfMemoryError error) {
        return false;

    } finally {
        if (expectedBitmap != null) {
            expectedBitmap.recycle();
        }
    }
}

那么您可以做:

onView(new RecyclerViewMatcher(R.id.rv_sub_services).atPositionOnView(0, R.id.iv_service_image)).check(matches(hasImage(R.drawable.eap_financial));

我不确定它是否如您所愿,但祝您好运!