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

时间:2017-09-16 07:48:07

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

if not view_id:
        view_id = self.env.ref('appartment.appartment_view_form').id
    result = super(Appartments, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
    doc = etree.XML(result['arch'])
    node = doc.xpath("//field[@name='appartmentrooms_id']/tree/field[@name='partner_ids']")
    print'node',node //Prints empty list

我有一个以下视图持有者,它使用滑动库加载图像URL。我正试图找到一种单元测试的方法:

Android Studio 3.0 Beta 5
robolectric:3.3.1

这是我所做的单元测试,但我不知道如何对图像视图进行单元测试。我不确定使用Mockito模拟Glide库会起作用吗?

    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());
        }
    }

}

输出:

@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");
}

非常感谢任何建议。

2 个答案:

答案 0 :(得分: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是否真的被填充了那个可画的。这是完全相同的,你验证你的抽象方法被调用(在上面提到的案例中ImageLoader#load())?因此,没有必要明确检查ImageView是否已填入Drawable,因为只要您同时嘲笑该组件,它也一定会被填充。

  

我想这意味着嘲笑Glide

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

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

答案 1 :(得分:2)

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