Android Studio 3.0 Beta 5
robolectric:3.3.1
グライドライブラリを使用して画像の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>
提案をありがとう。
しかし、画像ビューをユニットテストするにはどうすればよいかわかりません
あなたは間違った方向に進んでいると思います。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が呼び出されたことを確認するだけでした。
これらの2つのステートメントは基本的に同じです。正しいパラメーターを使用してジョブを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
からSomeAwesomeImageLoder
に移動することにした場合はどうなりますか?テストだけでなく、ソース内のすべてを変更する必要があります。
一方、画像の読み込みを担当するクラスがある場合は、読み込みロジックをそのクラス内にのみカプセル化するため、このクラスのみを変更する必要があります。また、これにより、ユニットテストを実行するための完璧な継ぎ目が得られます。
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
で機能します