单元测试 Glide:确保 ImageView 有正确的图像
Unit test Glide: make sure ImageView has correct image
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>
非常感谢您的任何建议。
在 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
but I am not sure how can I unit test the image view
我认为你走错了路:你想测试 Glide 是否按预期工作。作为该图书馆的客户,这不是您的责任。 Glide 有自己的测试来验证它是否按预期工作,您应该只对您在应用程序中实现的逻辑进行单元测试。
然而,如果您仍然想做类似的事情,那么您必须在 ViewHolder
中引入一些分离:一个负责将图像加载到 ImageView
.[=35= 中的组件]
public interface ImageLoader {
void load(Context context,
String path,
@DrawableRes int placeholder,
ImageView imageView);
}
谁的实现在class:
之后
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
class。
现在开始测试。这是设置:
@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
.
的逻辑
I wasn't trying to test the Glide API, but only to test that the image was successfully loaded into the imageview, or maybe just make sure the glide was called with the correct parameters.
这两个语句基本上是同一件事:如果你验证你将作业委托给 Glide 并使用正确的参数,那么它会验证 Glide 将正确加载图像。
现在,问题归结为如何验证您是否使用正确的参数将作业委托给 Glide?
在上述场景中:
holder.populateActor(actor);
verify(imageLoader).load(activity, path, R.drawable.people_placeholder, holder.imageView);
这将检查是否使用这些参数查询了 imageLoader
。
just trying to find the best way to ensure the image view has an image
你想要的是创建一个抽象,用一些模拟 Drawable
填充 ImageView
,然后在你的测试中你会检查 ImageView
是否真的被充满了那个drawable。这不完全一样吗,您验证抽象的方法是否被调用(在上述情况下 ImageLoader#load()
)?所以,没有必要明确地检查 ImageView
是否被 Drawable
填充,因为它肯定会,只要你也模拟了那个组件。
I guess this would mean mocking the Glide
不依赖实现,依赖抽象。如果您稍后决定从 Glide
移动到 SomeAwesomeImageLoder
怎么办?您必须更改源代码和测试中的所有内容。
另一方面,如果你有一个负责图像加载的 class,你将只在 class 中封装加载逻辑,因此只有这个 class需要改变。此外,这为执行单元测试提供了完美的接缝。
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>
非常感谢您的任何建议。
在 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
but I am not sure how can I unit test the image view
我认为你走错了路:你想测试 Glide 是否按预期工作。作为该图书馆的客户,这不是您的责任。 Glide 有自己的测试来验证它是否按预期工作,您应该只对您在应用程序中实现的逻辑进行单元测试。
然而,如果您仍然想做类似的事情,那么您必须在 ViewHolder
中引入一些分离:一个负责将图像加载到 ImageView
.[=35= 中的组件]
public interface ImageLoader {
void load(Context context,
String path,
@DrawableRes int placeholder,
ImageView imageView);
}
谁的实现在class:
之后
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
class。
现在开始测试。这是设置:
@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
.
I wasn't trying to test the Glide API, but only to test that the image was successfully loaded into the imageview, or maybe just make sure the glide was called with the correct parameters.
这两个语句基本上是同一件事:如果你验证你将作业委托给 Glide 并使用正确的参数,那么它会验证 Glide 将正确加载图像。
现在,问题归结为如何验证您是否使用正确的参数将作业委托给 Glide?
在上述场景中:
holder.populateActor(actor);
verify(imageLoader).load(activity, path, R.drawable.people_placeholder, holder.imageView);
这将检查是否使用这些参数查询了 imageLoader
。
just trying to find the best way to ensure the image view has an image
你想要的是创建一个抽象,用一些模拟 Drawable
填充 ImageView
,然后在你的测试中你会检查 ImageView
是否真的被充满了那个drawable。这不完全一样吗,您验证抽象的方法是否被调用(在上述情况下 ImageLoader#load()
)?所以,没有必要明确地检查 ImageView
是否被 Drawable
填充,因为它肯定会,只要你也模拟了那个组件。
I guess this would mean mocking the Glide
不依赖实现,依赖抽象。如果您稍后决定从 Glide
移动到 SomeAwesomeImageLoder
怎么办?您必须更改源代码和测试中的所有内容。
另一方面,如果你有一个负责图像加载的 class,你将只在 class 中封装加载逻辑,因此只有这个 class需要改变。此外,这为执行单元测试提供了完美的接缝。