Angelina
Angelina

Reputation: 1583

Unit testing Retrofit calls when service is injected with Dagger 2

I use Dagger 2 to provide all my classes in Android app. I want to unit test my repository class which has Retrofit service in the constructor.

class MainRepository(private val service: ImageService) {

fun download(username: String, liveData: MutableLiveData<ImageDownloadResult>) {
    service.downloadImage(username).enqueue(object : Callback<ImageResponse> {
        override fun onFailure(call: Call<ImageResponse>, t: Throwable) {
            liveData.value = DownloadFailed(t)
        }

        override fun onResponse(call: Call<ImageResponse>, response: Response<ImageResponse>) {
            if (response.isSuccessful) {
                response.body()?.let {
                    liveData.value = DownloadSuccessful(it.image)
                }
            } else {
                liveData.value = DownloadFailed(
                    when (response.code()) {
                        401 -> Unauthorized()
                        403 -> Forbidden()
                        404 -> NotFound()
                        in 500..599 -> InternalServerError()
                        else -> General()
                    }
                )
            }
        }
    })
}

}

Network module class provides the service in this way:

@Provides
@Singleton
fun provideImageService(gson: Gson, okHttpClient: OkHttpClient): ImageService {
    return Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create(gson))
        .baseUrl(mBaseUrl)
        .client(okHttpClient)
        .build()
        .create(ImageService::class.java)
}

I am trying to use Mockito to mock the classes, but I get NullPointerException in the download method.

public class MainRepositoryTest {

@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@InjectMocks
ImageService service;

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

@Test
public void testImageDownload() {
    MutableLiveData<ImageDownloadResult> liveData = new MutableLiveData<>();
    Response<ImageResponse> response = null;
    try {
        response = service.downloadImage("test").execute();
        assertEquals(response.code(), 200);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

Please educate me how to inject service into the test so that I can call Http calls there. Thank you!

Upvotes: 0

Views: 1818

Answers (1)

Fred
Fred

Reputation: 17095

I think you're confusing some bits here. In the test you seem to be testing the service, rather than the repository. The service needs to be mocked in order to actually return something and not null.

Let's start with the test setup. You'll have to declare the mocks and the thing you're testing

public class MainRepositoryTest {

  @Rule
  public MockitoRule mockitoRule = MockitoJUnit.rule();
  @Mock
  ImageService service;
  @InjectMocks
  MainRepository repository;

   //...
 }

The repository is what you actually will be testing here. As it is, you just get an instance of the repository with the service mock injected. However, this mock will return null on every method call.

So now, you'll need to set it up for each case you want to test. Say you want to test a successful response:

@Test
public void testSuccess (){
    when(service.downloadImage("test")).theReturn(<successful response>);

     repository.download("test", <mutable live data>);

     // Now assert something about the live data
}

You'll have to mock the service to return a successful response when called with the expected arguments. I'm not sure how to use live data here, because I never used it. I actually thought retrofit supported this already and that you wouldn't have to manually convert it.

As for dagger, it's great that you're using dependency injection. However, in tests usually you don't inject dependencies with dagger. Using the mockito annotation is enough here.

Upvotes: 2

Related Questions