2017-09-16 5 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를 사용하는 것이 효과가 있을지 모르겠습니다.

@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를 테스트하는 것을 시도하지 않은,하지만 이미지가 성공적으로 이미지 뷰에로드, 아니면 그냥 글라이드가 올바른 매개 변수와 함께 호출 된 확인 된 것을 테스트 할 수 있습니다.

두 명령문은 기본적으로 같은 의미입니다. 올바른 매개 변수를 사용하여 작업을 글라이드에 위임했는지 확인하는 경우 글라이드가 이미지를 올바르게로드하는지 확인하는 것입니다.

이제 글라이드에 올바른 매개 변수를 사용하여 작업을 위임하는지 확인하는 방법에 대한 질문이 나옵니 까? 위에서 언급 한 시나리오에서

는 :

 

    holder.populateActor(actor); 

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

이 그 매개 변수를 사용하여 쿼리 된 imageLoader 여부를 확인합니다.

그냥 이미지 뷰를 보장하는 최선의 방법을 찾기 위해 노력하는 것은 당신이 원하는 것은 추상화를 만드는 것입니다

, 그 어떤 모의 Drawable으로 ImageView 작성 및 내 것

이미지를 가지고 당신의 테스트 할 때 ImageView이 실제로 드로어 블로 채워 졌는지 확인하십시오. 이것은 정확히 동일하지 않으므로 추상화에 대한 메서드가 호출되었는지 (위에서 언급 한 경우 ImageLoader#load()) 확인합니까? 그러므로 ImageViewDrawable으로 채워 졌는지 여부를 명시 적으로 확인할 필요가 없습니다. 왜냐하면 해당 구성 요소도 조롱했기 때문입니다.

내가이 글라이드

구현에 의존하지 마십시오 조롱하는 의미 추측, 추상화에 따라 달라집니다. 나중에 Glide에서 SomeAwesomeImageLoder으로 이동하려면 어떻게해야하나요? 소스와 테스트 내 모든 것을 변경해야합니다.

반면에 이미지로드를 담당하는 클래스가있는 경우 해당 클래스 내에서만로드 논리를 캡슐화하므로이 클래스 만 변경해야합니다. 또한 이것은 단위 테스트를 수행하기위한 완벽한 솔기를 제공합니다.

+1

좋은 답변, 글라이드 유창한 API를 4-5 매개 변수로 인터페이스 메소드로 변경하는 것을 싫어합니다. 그래서 조금 더 나은 대답은 글라이드 API –

+0

@azizbekian을 모방 한 유창한 API 인터페이스를 제어 할 수 있었을 것입니다. 사실, 어쩌면 내가 성취하려고했던 것을 다시 말해야했습니다. 글라이드 API를 테스트하려하지 않고 이미지 뷰에 이미지가 있는지 확인하거나 글라이드가 올바른 매개 변수로 호출되었는지 확인하려고했습니다 (글라이드 조롱을 의미하는 것 같습니다). 이게 더 명확 해지기를 바랍니다. 감사합니다 – ant2009

+0

@ ant2009, 업데이트. – azizbekian

2

Robolectric에서 drawableimageview으로 설정하고 그에 대한 어설 션을 설정할 수 있습니다. 귀하의 경우에는

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을 비교하는 것 같습니다. drawable과 int를 비교하는 것 같습니다. – ant2009