20

I'm mocking the response of the APIService. Unfortunately it is not working, I have to send back a Call but I don't understand how. The question is how to send back a Call object.

@RunWith(AndroidJUnit4::class)
class ApiServiceTest {

    @Test
    fun testSomething() {
        val apiService = ApiServiceMock()
        val call = apiService.getStep1User()
        val result = call.execute()
        Assert.assertEquals("SomeUserValue", result.body()!!.getResponse())
    }
}

Here is the mocked service:

class ApiServiceMock : ApiService {
    override fun getStep1User(): Call<UserResponse> {
        // How to return an object of type Call<UserResponse> ?
        val response = "{ \"Response\": \"SomeUserValue\" }"
        val gson = Gson().toJson(response)
        return Response.success(gson)
    }
}

Here is the api interface:

interface ApiService {

    @GET("/booky/step1user")
    fun getStep1User(): Call<UserResponse>

    companion object {

        val interceptor = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
        val client = OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .build()

        val retrofit = Retrofit.Builder()
                .baseUrl("http://jimclermonts.nl")
                .addConverterFactory(MoshiConverterFactory.create().asLenient())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(client)
                .build()
    }
}

build.gradle:

implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.3.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
implementation "com.squareup.retrofit2:adapter-rxjava2:2.3.0"

implementation 'com.google.code.gson:gson:2.8.0'

testImplementation "org.mockito:mockito-core:2.12.0"
testImplementation "com.nhaarman:mockito-kotlin:1.5.0"
implementation 'org.mockito:mockito-android:2.18.0'
Jim Clermonts
  • 1,694
  • 8
  • 39
  • 94
  • How does your UserResponse class look? – TpoM6oH Apr 18 '18 at 14:08
  • Please specify what is your goal: what are you trying to test? – azizbekian Apr 19 '18 at 06:51
  • When you execute network call, it is going to take a time, so I just want to add I think you should use trampoline(first in first out) schedule, because junit will compare answer, but yet server did not respond, that may cause issue. I am not sure, when i used rx java I had to do job schedule not in background. – Daniyar Kayirbolatov Apr 25 '18 at 07:31

4 Answers4

16

Call is an interface, you can create an object that implements it and return it from your mocking method:

class ApiServiceMock : ApiService {
    override fun getStep1User(): Call<UserResponse> {
        return object: Call<SignedUserUi> {
            override fun enqueue(callback: Callback<UserResponse>?) {
            }

            override fun isExecuted(): Boolean {
                return false
            }

            override fun clone(): Call<UserResponse> {
                return this
            }

            override fun isCanceled(): Boolean {
                return false
            }

            override fun cancel() {

            }

            override fun request(): Request {
                return Request.Builder().build()
            }

            override fun execute(): Response<UserResponse> {
                // Create your mock data in here
                val response = "{ \"Response\": \"SomeUserValue\" }"
                val gson = Gson().toJson(response)
                return Response.success(UserResponse(gson))
            }

        }
    }
}

If you want to have less boilerplate and be able to mock interfaces in a single line I would recommend you to take a look at mockito for kotlin.

After including it to your project you'll be able to do

val rawResponse = "{ \"Response\": \"SomeUserValue\" }"
val gson = Gson().toJson(rawResponse)
val response = Response.success(UserResponse(gson))

val mockCall = mock<Call<UserResponse>> {
    on { execute() } doReturn response
}

val mockApiService = mock<ApiService> {
    on { getStep1User() } doReturn mockCall
}
TpoM6oH
  • 8,385
  • 3
  • 40
  • 72
7

You can use RETURNS_DEEP_STUBS. But not sure how does it work in Kotlin.

mock = Mockito.mock(Api.class, RETURNS_DEEP_STUBS)
when(mock.getSomething().execute()).thenReturn(Response.success(...));
radeklos
  • 2,168
  • 21
  • 19
6

What you are trying to do is testin retrofit ! You must assert on the behavior of your application after getting the response, not asserting on what retrofit get as response of the request!! For example assert that an error response, an error dialog must appear.

You can mock the response using OkHTTPMock server. add the dependency in your build.gradle module file:

testImplementation 'com.squareup.okhttp3:mockwebserver:lastVersion'

and then in your test file you can mock a server, a request and a response. here an example:

MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse().setBody("{ \"Response\": \"SomeUserValue\" }"));
// Start the server.
  server.start();

//and than load your request. Be Careful, they are executed in the order that you enqued them!

//Add your assertion (Succes response and error response)
//if you are working with MVP architecture, you can assert for the success case
//that method showData(data) is called using Mockito.
verify(myPresenter).showData(data);

Take a look at the officiel example of OkHttpMock

Fakher
  • 2,098
  • 3
  • 29
  • 45
2

You need to use a helper class to mock responses.

class CallFake<T>(
    private val response: Response<T>) 
: Call<T> {

companion object {
    inline fun <reified T> buildSuccess(body: T): CallFake<T> {
        return CallFake(Response.success(body))
    }

    inline fun <reified T> buildHttpError(errorCode: Int, contentType: String, content: String): CallFake<T> {
        return CallFake(Response.error(errorCode, ResponseBody.create(MediaType.parse(contentType), content)))
    }
}

override fun execute(): Response<T> = response

override fun enqueue(callback: Callback<T>?) {}

override fun isExecuted(): Boolean = false

override fun clone(): Call<T> = this

override fun isCanceled(): Boolean = false

override fun cancel() {}

override fun request(): Request? = null 
}

and then in your test class, you have to use the function when as shown below to say what to return when calling apiService.

@RunWith(MockitoJUnitRunner::class)
class ApiServiceTest {
@Mock
lateinit var apiService: ApiService

@Test
fun testSomething() {
    Mockito.`when`(apiService.getStep1User())
            .thenReturn(CallFake.buildSuccess(UserResponse("SomeUserValue")))

    val call = apiService.getStep1User()
    val response = call.execute()
    val userResponse = response.body() as UserResponse

    Assert.assertEquals("SomeUserValue", userResponse.userValue)
}
}
Daniel RL
  • 353
  • 1
  • 12