单元测试Glide:确保ImageView具有正确的图像

ant*_*009 14 testing android unit-testing robolectric android-glide

Android Studio 3.0 Beta 5
robolectric:3.3.1
Run Code Online (Sandbox Code Playgroud)

我有一个以下视图持有者使用滑动库加载图像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());
        }
    }
Run Code Online (Sandbox Code Playgroud)

这是我所做的单元测试,但我不知道如何对图像视图进行单元测试.我不确定使用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");
}
Run Code Online (Sandbox Code Playgroud)

}

输出:

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>
Run Code Online (Sandbox Code Playgroud)

非常感谢任何建议.

azi*_*ian 21

但我不知道如何对图像视图进行单元测试

我认为你的方法是错误的:你想测试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?

在上面提到的场景中:


    holder.populateActor(actor);

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

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

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

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

我想这意味着嘲笑Glide

不依赖于实现,依赖于抽象.如果你以后决定从移动GlideSomeAwesomeImageLoder?您必须更改源和测试中的所有内容.

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