2017-09-16 9 views
10
Android Studio 3.0 Beta 5 
robolectric:3.3.1 

グライドライブラリを使用してイメージURLをロードする以下のビューホルダーがあります。ユニットテストグライド:ImageViewが正しいイメージを持っていることを確認してください。

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 <[email protected]> 
    but: was <[email protected]> 
Expected :is <[email protected]> 

Actual :<[email protected]> 

感謝します。

+0

それはネットワークやリソースから画像をロードしていますか? –

答えて

18

で動作しますが、私はユニット画像ビューをテストすることができますかわかりませんさ

あなたは間違った方向にいると思います:あなたはグライドi期待どおりに働いています。これは、その図書館のクライアントとしてのあなたの責任ではありません。グライドはそれが期待どおりに動作することを確認する独自のテストを持っていますので、アプリケーション内で実装するロジックを単体テストするだけです。

ViewHolderに画像を読み込むコンポーネント: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はこのようなものになるだろう:誰の実装クラスを以下れる

 

    public interface ImageLoader { 

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

 

    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にダウンロードする方法の論理を無視することです。


私はグライドAPIをテストしようとしていなかったが、画像のみが正常にImageViewのにロードされた、または多分ちょうどグライドが正しいパラメータで呼び出されたことを確認したことをテストします。

これらの2つのステートメントは、基本的には同じものです:あなたはそれがグライドが正しくイメージをロードすることを確認するよりも、正しいパラメータでグライドするジョブを委任することを確認した場合。

さて、問題は、あなたが正しいパラメータでグライドする仕事を委任されていることを確認する方法に帰着しますか?上記のシナリオで

 

    holder.populateActor(actor); 

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

これはimageLoaderはこれらのパラメータを使用して照会されたかどうか、チェックします。

だけで画像表示を確保するための最良の方法を見つけようとすると、何をしたい画像

は、いくつかのモックDrawableImageViewを埋めるだろうと、抽象化を作成することで、あなたの内にいますImageViewが実際にそのドロウアブルで満たされているかどうかをチェックします。これはまったく同じではありません。あなたの抽象メソッドが(上記のケースではImageLoader#load())呼び出されたことを確認しますか?だから、明示的に確認するのでImageViewは、限り、あなたはまた、そのコンポーネントを嘲笑しているように、Drawableで満たされたかどうかをチェックする必要はありません。

私は、これは抽象化に依存し、実装に依存しないでくださいグライド

をからかっ意味するだろうと思います。後でGlideからSomeAwesomeImageLoderに移動する場合はどうなりますか?ソースとテストのすべてをテストと同様に変更する必要があります。

一方

、あなたは画像の読み込みを担当したクラスを持っている場合、あなたは、このようにだけ、このクラスには変更が必要になる、それだけでクラス内のロードロジックをカプセル化すると思います。また、これは単体テストを実行するための完璧なシームを提供します。

+1

良い答え、私は、Glide Fluent APIを4-5パラメータでインターフェイスメソッドに変更したくない。だから、少し良く答えは模倣は、偉大な答えに感謝をAPI –

+0

@azizbekianグライド流暢なAPIとのインタフェースを制御しているだろう。実際には、私が達成しようとしていたことを言い返すべきだったかもしれません。私はちょうど画像ビューは、画像があることを確認、またはグライドが正しいパラメータで呼び出されたことを確認するための最良の方法を見つけようと、グライドAPIをテストしようとしていませんでした(私はこれはグライドをからかっ意味するであろうと思います)。私はこれがより明確であることを望む。ありがとう – ant2009

+0

@ ant2009、更新しました。 @アジズベキアン。 – azizbekian

2

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

+0

@ ant2009あなたの問題は解決していませんか? –

+0

次のような問題が発生します:<2130837650> 実際:<[email protected]>私は実際の描画可能ファイルとリソースIDを比較しているようです。私はdrawableとintを比較しているようです。 – ant2009

関連する問題