Search code examples
androidtestingmockingmockitopicasso

How to properly mock Picasso into Unit Tests?


As I am trying to conduct the following simple Presenter Test

public class NewsPresenterTest {
private static List<News> NEWS_HEADLINES;

@Mock
private NewsRepository mNewsRepository;

@Mock
private Picasso mPicassoClient;

@Mock
private ChromeTabsWrapper mChromeTabsWrapper;

@Mock
private NewsContract.View mNewsView;

@Captor
private ArgumentCaptor<NewsDataSource.LoadNewsCallback> mLoadNewsCallbackCaptor;

private NewsPresenter mNewsPresenter;

@Before
public void setupNewsPresenter() {
    // inject the mocks
    MockitoAnnotations.initMocks(this);

    mNewsPresenter = new NewsPresenter(mNewsRepository, new CompositeDisposable(), mPicassoClient, mChromeTabsWrapper);
    mNewsPresenter.subscribe(mNewsView);

    NEWS_HEADLINES = Lists.newArrayList(...);
}

@Test
public void loadHeadlinesNewsFromRepositoryAndLoadIntoView(){
          // verify certain behavior 
    }
}

I seem to not be able in mocking mPicassoClient, as result getting the following exception:

java.lang.ExceptionInInitializerError
at sun.reflect.GeneratedSerializationConstructorAccessor3.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
...more 
Caused by: java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.os.Looper.getMainLooper(Looper.java)
at com.squareup.picasso.Picasso.<clinit>(Picasso.java:109)
... and more

As the stack states, the exception occurs when mocks are being initiated, to be more accurate on this line MockitoAnnotations.initMocks(this);

Therefore mocking Picasso.class fails. What am I doing wrong?


Solution

  • The Picasso class doesn't belong in your presenter. Move the call to Picasso inside your Fragment. Note that this doesn't prevent it from being covered by the test (in a less explicit way):

    Previously:

    newsRepository.getNews(date) 
       .subscribe(news ->
          Picasso.loadImage(news.getImageUrl()))
    

    Refactored:

    newsRepository.getNews(date)
        .subscribe(news ->
            view.loadImage(news.getImageUrl()))
    

    Where view is the View from Model/View/Presenter and is implemented by your Activity or Fragment. Inside your Fragment or Activity:

    @Override
    public void loadImage(String url) {
        Picasso.loadImage(url);
    }