【问题标题】:Unit test Glide: make sure ImageView has correct image单元测试 Glide:确保 ImageView 具有正确的图像
【发布时间】:2018-02-25 08:26:30
【问题描述】:
Android Studio 3.0 Beta 5
robolectric:3.3.1

我有一个使用 glide 库加载图像 url 的以下视图持有者。我正在尝试找到一种对此进行单元测试的方法:

    public class MovieActorsViewHolder extends RecyclerView.ViewHolder {
        @BindView(R.id.civActorPicture) CircleImageView actorPicture;
        @BindView(R.id.tvName) TextView name;
        @BindView(R.id.tvCharacter) TextView character;

        private Context context;

        public MovieActorsViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);

            context = itemView.getContext();
        }

        public void populateActor(Actor actor) {
            Glide.with(context)
                    .load(actor.getPicturePath())
                    .placeholder(R.drawable.people_placeholder)
                    .into(actorPicture);

            name.setText(actor.getName());
            character.setText(actor.getCharacter());
        }
    }

这是我所做的单元测试,但我不确定如何对图像视图进行单元测试。我不确定使用 Mockito 来模拟 Glide 库是否可行?

@RunWith(RobolectricTestRunner.class)
public class MovieActorsViewHolderTest {
    private MovieActorsViewHolder movieActorsViewHolder;

    @Before
    public void setup() {
        final Context context = ShadowApplication.getInstance().getApplicationContext();
        final View view = LayoutInflater.from(context).inflate(R.layout.movie_actors_item, new LinearLayout(context));

        movieActorsViewHolder = new MovieActorsViewHolder(view);
    }

    @Test
    public void testShouldPopulateActorWithValidData() {
        final Actor actor = getActor();
        movieActorsViewHolder.populateActor(actor);

        /* test that the image view */
    final ShadowDrawable shadowDrawable = Shadows.shadowOf(movieActorsViewHolder.actorPicture.getDrawable());
    final Drawable drawable = Drawable.createFromPath(actor.getPicturePath());
    assertThat(drawable, is(shadowDrawable.getCreatedFromResId()));

        assertThat(movieActorsViewHolder.name.getText(), is(actor.getName()));
        assertThat(movieActorsViewHolder.character.getText(), is(actor.getCharacter()));
    }

  private Actor getActor() {
    return new Actor(
            "https://image.tmdb.org/t/p/w92/dRLSoufWtc16F5fliK4ECIVs56p.jpg",
            "Robert Danny Junior",
            "Iron Man");
}

}

输出:

Expected: is <org.robolectric.shadows.ShadowBitmapDrawable@ffffffe0>
     but: was <android.graphics.drawable.BitmapDrawable@ffffffe0>
Expected :is <org.robolectric.shadows.ShadowBitmapDrawable@ffffffe0>

Actual   :<android.graphics.drawable.BitmapDrawable@ffffffe0>

非常感谢您的任何建议。

【问题讨论】:

  • 是从网络加载图片还是从资源加载图片?

标签: android unit-testing testing robolectric android-glide


【解决方案1】:

Robolectric 中,您可以将drawable 设置为您的imageview 并对其进行断言。

ShadowDrawable shadowDrawable = Shadows.shadowOf(imageView.getDrawable());
        assertEquals(expected, shadowDrawable.getCreatedFromResId());

在你的情况下,你可以这样做 -

final Actor actor = getActor();
movieActorsViewHolder.populateActor(actor);
ShadowDrawable shadowDrawable = Shadows.shadowOf(movieActorsViewHolder.actorPicture.getDrawable());
Drawable expected = Drawable.createFromPath(actor.getPicturePath());
assertEquals(expected, shadowDrawable.getCreatedFromResId());

注意:这是在Robolectric 3.3.1 测试和工作

【讨论】:

  • 有一个问题我得到以下预期:is 实际: 看来我只是在比较实际可绘制而不是资源ID。看起来我正在将可绘制对象与 int 进行比较。
【解决方案2】:

但我不确定如何对图像视图进行单元测试

我认为你走错路了:你想测试 Glide 是否按预期工作。作为该图书馆的客户,这不是您的责任。 Glide 有自己的测试来验证它是否按预期工作,你应该只对你在应用中实现的逻辑进行单元测试。

尽管如此,如果您仍想做类似的事情,则必须在 ViewHolder 中引入一些分隔:负责将图像加载到 ImageView 的组件。

public interface ImageLoader { void load(Context context, String path, @DrawableRes int placeholder, ImageView imageView); }

谁的实现跟随类:

public class ImageLoaderImpl implements ImageLoader { @Override public void load(Context context, String path, int placeholder, ImageView imageView) { Glide.with(context) .load(path) .placeholder(placeholder) .into(imageView); } }

现在你的ViewHolder 会变成这样:

class MovieActorsViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.picture) ImageView imageView; // other views ImageLoader imageLoader; MovieActorsViewHolder(View itemView, ImageLoader imageLoader) { super(itemView); ButterKnife.bind(this, itemView); this.imageLoader = imageLoader; } void populateActor(Actor actor) { imageLoader.load(itemView.getContext(), actor.getPicturePath(), R.drawable.people_placeholder, imageView); // other actions } }

这将使您可以灵活地模拟 ImageLoader 类。

现在开始测试。这是设置:

@Before public void setup() { imageLoader = Mockito.mock(ImageLoader.class); activity = Robolectric.setupActivity(MainActivity.class); ViewGroup root = (ViewGroup) activity.findViewById(R.id.root); View inflated = activity.getLayoutInflater().inflate(R.layout.item, root); holder = new MovieActorsViewHolder(inflated, imageLoader); }

这里是测试方法:

@Test public void test() throws InterruptedException { final String path = "https://image.tmdb.org/t/p/w92/dRLSoufWtc16F5fliK4ECIVs56p.jpg"; final Actor actor = new Actor(path); final Bitmap bitmap = Shadow.newInstanceOf(Bitmap.class); final BitmapDrawable drawable = new BitmapDrawable(activity.getResources(), bitmap); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { holder.imageView.setImageDrawable(drawable); return null; } }).when(imageLoader).load(activity, path, R.drawable.people_placeholder, holder.imageView); holder.populateActor(actor); assertEquals(holder.imageView.getDrawable(), drawable); }

这会过去的。但是问问自己:你用这个测试了什么?相反,更好的测试是确保 imageLoader.load(...) 已使用正确的参数调用,忽略 Glide 如何将该图像下载到 ImageView 的逻辑。


我并没有尝试测试 Glide API,而只是测试图像是否已成功加载到 imageview 中,或者只是确保使用正确的参数调用了 glide。

这两个语句基本上是同一件事:如果您验证您使用正确的参数将作业委托给 Glide,那么它验证 Glide 将正确加载图像。

现在,问题归结为如何验证您将作业委派给 Glide 并使用正确的参数?

在上述场景中:

holder.populateActor(actor); verify(imageLoader).load(activity, path, R.drawable.people_placeholder, holder.imageView);

这将检查是否使用这些参数查询了 imageLoader

只是试图找到确保图像视图有图像的最佳方法

您想要创建一个抽象,用一些模拟Drawable 填充ImageView,并在您的测试中检查ImageView 是否实际上已填充了该可绘制对象。这不完全一样,你验证你的抽象方法被调用了(在上面提到的情况下ImageLoader#load())?所以,没有必要明确检查ImageView是否被Drawable填充,因为它肯定会,只要你也模拟了那个组件。

我猜这意味着嘲笑 Glide

不依赖实现,依赖抽象。如果您后来决定从Glide 移动到SomeAwesomeImageLoder 怎么办?您必须更改源代码和测试中的所有内容。

另一方面,如果您有一个负责图像加载的类,您只需将加载逻辑封装在该类中,因此只有该类需要更改。此外,这为执行单元测试提供了完美的接缝。

【讨论】:

  • 好答案,我只是不喜欢将 Glide fluent API 更改为具有 4-5 个参数的接口方法。因此,更好的答案是使用模仿 Glide API 的流式 API 来控制接口
  • @azizbekian,感谢您的出色回答。实际上,也许我应该重新措辞我想要达到的目标。我并没有试图测试 Glide API,只是试图找到确保图像视图有图像的最佳方法,或者确保使用正确的参数调用滑翔(我想这意味着模拟 Glide)。我希望这更清楚。谢谢
  • @azizbekian。这是一个非常详细和出色的答案。这就是我要找的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-25
  • 1970-01-01
  • 1970-01-01
  • 2011-09-24
  • 2014-11-07
  • 2022-01-14
  • 2015-07-24
相关资源
最近更新 更多