Good way to unit test Retrofit interface declaration - android

I have next interface declaration:
public interface FundaService
{
#GET( "/feeds/Aanbod.svc/json/{key}" )
Observable<JsonResponse> queryData( #Path( "key" ) String key, #Query("type" ) String type, #Query( "zo" ) String search, #Query( "page" ) int page, #Query( "pagesize" ) int pageSize );
}
That I use after with Retrofit. What would be an elegant way of testing that I didn't make mistakes in URL declaration and query parameters?
I see that I can mock web layer and check urls with parameters.
UPDATE
I modified it:
public interface FundaService
{
String KEY_PATH_PARAM = "key";
String FEED_PATH = "/feeds/Aanbod.svc/json/{" + KEY_PATH_PARAM + "}";
String TYPE_QUERY_PARAM = "type";
String SEARCH_QUERY_PARAM = "zo";
String PAGE_QUERY_PARAM = "page";
String PAGESIZE_QUERY_PARAM = "pagesize";
#GET( FEED_PATH )
Observable<JsonResponse> queryData( #Path( KEY_PATH_PARAM ) String key, #Query( TYPE_QUERY_PARAM ) String type,
#Query( SEARCH_QUERY_PARAM ) String search, #Query( PAGE_QUERY_PARAM ) int page,
#Query( PAGESIZE_QUERY_PARAM ) int pageSize );
}
And partially testing it, like:
public class FundaServiceTest
{
#Test
public void PathKeyIsCorrect()
throws Exception
{
assertThat( FundaService.KEY_PATH_PARAM ).isEqualTo( "key" );
}
#Test
public void FeedPathIsCorrect()
throws Exception
{
assertThat( FundaService.FEED_PATH ).isEqualTo( "/feeds/Aanbod.svc/json/{key}" );
}
}

You can use an okhttp interceptor to inspect the final request built by retrofit without using a mock http server. It gives you a chance to inspect the request a bit earlier. Suppose we want to test the following interface -
public interface AwesomeApi {
#GET("/cool/stuff")
Call<Void> getCoolStuff(#Query(("id"))String id);
}
The first test runs 'validateEagerly` to do a validation of the entire interface. Useful to have in case your other test cases don't touch all the interface methods. The second test is an example of how you might verify a specific call is generating the expected url.
public class AwesomeApiTest {
#Test
public void testValidInterface() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.example.com/")
.addConverterFactory(GsonConverterFactory.create())
// Will throw an exception if interface is not valid
.validateEagerly()
.build();
retrofit.create(AwesomeApi.class);
}
#Test(expected = NotImplementedException.class)
public void testCoolStuffRequest() throws Exception {
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new Interceptor() {
#Override
public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
final Request request = chain.request();
// Grab the request from the chain, and test away
assertEquals("HTTP methods should match", "GET", request.method());
HttpUrl url = request.httpUrl();
// Test First query parameter
assertEquals("first query paramter", "id", url.queryParameterName(0));
// Or, the whole url at once --
assertEquals("url ", "http://www.example.com/cool/stuff?id=123", url.toString());
// The following just ends the test with an expected exception.
// You could construct a valid Response and return that instead
// Do not return chain.proceed(), because then your unit test may become
// subject to the whims of the network
throw new NotImplementedException();
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
AwesomeApi awesomeApi = retrofit.create(AwesomeApi.class);
awesomeApi.getCoolStuff("123").execute();;
}
}
I got this idea from browsing retrofit's own tests. Other people's tests are often great inspiration!

Related

Read plain text response from server using Retrofit

I'm working on an application that uses Retrofit for network operations. As it stands, everything works well with GsonConverterFactory handling serialization. Here is how I setup Retrofit
Retrofit.Builder()
.baseUrl("<base url>")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
Now I need to connect to a legacy service which returns content in text/plain; charset=utf-8 format. Here is the Retrofit interface
#GET("https://<domain>/<endpoint>?Type=Query")
suspend fun callStatus(#Query("userId") id: Int): Response<String>
This will return status of a call for a valid user. For instance, if the user is valid and there is a status, it returns "Active" as plain text. If there is no valid user, it returns an error code of #1005
I could add custom converter factory like this (found on the web)
final class StringConverterFactory implements Converter.Factory {
private StringConverterFactory() {}
public static StringConverterFactory create() {
return new StringConverterFactory();
}
#Override
public Converter<String> get(Type type) {
Class<?> cls = (Class<?>) type;
if (String.class.isAssignableFrom(cls)) {
return new StringConverter();
}
return null;
}
private static class StringConverter implements Converter<String> {
private static final MediaType PLAIN_TEXT = MediaType.parse("text/plain; charset=UTF-8");
#Override
public String fromBody(ResponseBody body) throws IOException {
return new String(body.bytes());
}
#Override
public RequestBody toBody(String value) {
return RequestBody.create(PLAIN_TEXT, convertToBytes(value));
}
private static byte[] convertToBytes(String string) {
try {
return string.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
}
But I didn't see it make any difference. Also, it could well disguise JSON as normal text and break all existing service. Is there a better way to handle this scenario? I thought of having separate retrofit instance for plain text, bit dirty though. Do you have any other suggestions/solutions?
Edited
Response header contains the content type as
Content-Type: text/plain; charset=utf-8
Actual response for valid user
Active
Actual response for invalid user
#1005
Update
The order in which you register the converter factories matters. ScalarsConverterFactory must come first.
it should be possible by adding ScalarsConverterFactory when building the Retrofit object.
This can be done alongside with other json converters, e.g.
Retrofit.Builder()
.baseUrl("<base url>")
.client(client)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
After that, you should be able to receive plaintext responses.
You probably need to add this to your dependencies as well:
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
The following is the way that how I get response as plain text (using Java not Kotlin).
Step One
in your gradle (Module);
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
Step Two
Create an interface
public interface MyInterface {
#GET("something.php")
Call<String> getData(#Query("id") String id,
#Query("name") String name);
}
Step Three
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://example.com")
.addConverterFactory(ScalarsConverterFactory.create())
.build();
MyInterface myInterface = retrofit.create(MyInterface.class);
Call<String> call = myInterface.getData("id","myname");
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
String plain_text_response = response.body();
}
#Override
public void onFailure(Call<String> call, Throwable t) {
}
});
You don't need to use a your custom implementation of Converter.Factory you could just use
// your coroutine context
val response = callStatus(userId)
if(response.isSuccessful){
val plainTextContent = response.body()
// handle plainText
} else {
//TODO: Handle error
}
//...
Two things to check first that function should not be suspended & your response should be in the Callback
No need to add extra implementation of scalars.
#GET
fun getJson(
#Url baseUrl: String = slab_pro
): Call<DataClass>

How to send post request with basic auth in retrofit?

In my code, I want to send post request with basic auth.
Here is my postman screenshot :
here is my apiInterface class
#FormUrlEncoded
#POST("GetBarcodeDetail")
Call<PreliminaryGoodsAcceptResponse> PRELIMINARY_GOODS_ACCEPT_RESPONSE_CALL(#Field("ProcName") String procName, #Field("Barcode") String barcode, #Field("LangCode") String langCode);
here is my apiclient
public class ApiClient {
public static final String BASE_URL = "http://192.**********";
private static Retrofit retrofit = null;
private static OkHttpClient sClient;
public static Retrofit getClient() {
if(sClient == null) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
sClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT))
.addInterceptor(interceptor)
.build();
}
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(sClient)
.build();
}
return retrofit;
}
}
My question is how can i send post request,using header :
Header Username : EBA Token :
34242353453456563DSFS
This is so far the easiest method i have ever tried for "Basic Authentication".
Use the below code to generate the auth header (API/Repository class)
var basic = Credentials.basic("YOUR_USERNAME", "YOUR_PASSWORD")
Pass this as header to the webservice call (API/Repository class)
var retrofitCall = myWebservice.getNewsFeed(basic)
Add the basic header as parameter (Retrofit Webservice interface class)
#GET("newsfeed/daily")
fun getNewsFeed(#Header("Authorization") h1:String):Call<NewsFeedResponse>
Sorry, my code is in Kotlin, but can be easily translated to Java.
References: https://mobikul.com/basic-authentication-retrofit-android/
make header like this way..
private Retrofit getClient(final Context context) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.readTimeout(60, TimeUnit.SECONDS);
client.writeTimeout(60, TimeUnit.SECONDS);
client.connectTimeout(60, TimeUnit.SECONDS);
client.addInterceptor(interceptor);
client.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (context == null) {
request = request
.newBuilder()
.build();
} else {
request = request
.newBuilder()
.addHeader("Authorization", "Bearer " + AppSetting.getStringSharedPref(context, Constants.USER_KEY_TOKEN, ""))
.build();
}
return chain.proceed(request);
}
});
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
Use Header annotation
#FormUrlEncoded
#POST("GetBarcodeDetail")
Call<PreliminaryGoodsAcceptResponse> PRELIMINARY_GOODS_ACCEPT_RESPONSE_CALL(#Header("Authorization") token: String,#Field("ProcName") String procName, #Field("Barcode") String barcode, #Field("LangCode") String langCode);
Simple-Retrofit-API-request-and-Data-Loading Here I just add the project where create the API call to access data from database using retrofit library; which is leading library to access data on network. And display the accessed data in the List format. Create the Simple Android Studio Project with Empty Activity. Create the Adapter and activity item to show normal lists in android app. Now Create the App class extending Application, as Application class is a singleton that you can access from any activity or anywhere else you have a Context object.
You can check the more details about Application class from https://github.com/codepath/android_guides/wiki/Understanding-the-Android-Application-Class Why extend an Application class? https://developer.android.com/reference/android/app/Application.html
Add android:name=".YourApplication" i.e. class name extending the Application class in android. and class will be like public class YourApplication extends Application Init the Retrofit in Application class
//network code start
//init http logger
httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// init client client = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request request2 = request.newBuilder().build();
return chain.proceed(request2);
}
}).connectTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
Gson gson = new GsonBuilder().setLenient().create();
Retrofit mRetrofit = new Retrofit.Builder().baseUrl(Constants.API_BASE_URL).client(client).addConverterFactory(GsonConverterFactory.create(gson)).build();
mWebservice = mRetrofit.create(Webservice.class);
While Constants.API_BASE_URL is base url Create the Webervice.class where you can call the API with parameters e.g. In case of GET Method:
#GET("webservices/GetAllClientsDemoRetro.php")
Call updateChatStatus();
In case of POST method:
#FormUrlEncoded
#Headers({"Content-Type: application/x-www-form-urlencoded"})
#POST("webservices/GetAllClientsDemoRetro.php")
Call updateChatStatus();
You can See the more in details About Retrofit on Official API declaration here: http://square.github.io/retrofit/
We can parse the values with POJO i.e. Setter and Getter, using the Parceble class. Since parsing key name should be equal to the value we are receiving from the JSON response. POJO class should be declared like public class ClientData implements Parcelable { then declare the keys in the class, key values means
public class ClientData implements Parcelable
{
public String client_id;
public String company_name;
public String address_line;
public String city;
public String pincode;
public String state;
public String country;
}
Now using Alt+Enter i.e. select the option Add Parceble Implementation and press enter. Then automatically parceble class will be added. Also you have to add Setter and Getter method in class using Alt + Insert. Note: Don’t add the Setter and Getter methods for CREATER: Creater<> method If you want to use different key that JSON response key, then you should use Serialization. When I was using same key then its is like public String client_id; But when I am using the Serialization, then I can use like #Serializattion(“client_id”) public String ClientID; Now last but not a list, We call the API using retrofit, and use the response to view the Item in list-
RetroFitApplication.getWebservice().updateChatStatus().enqueue(new Callback() {
#Override public void onResponse(Call call, Response response) {
Log.d("retrofilt success", "" + response.body());
if (response.body() != null) {
clientResponceData = response.body();
Gson gson = new Gson();
String body = gson.toJson(response.body());
Log.d("retrofilt success2", "clientData" + clientResponceData.getResponse());
if (clientResponceData.getResponse() != null) {
initRV();
}
} else {
// Empty Client List Toast.makeText(ClientList.this, "Empty List", Toast.LENGTH_SHORT).show();
}
}
#Override public void onFailure(Call call, Throwable t) {
Log.d("retrofilt error", "" + t);
Toast.makeText(ClientList.this, "No Internet Connection", Toast.LENGTH_SHORT).show();
}
});
By using the Construction in Adapter, we can use the values from the response. Guys I added this repository to get the Entire idea of calling the API and get the response from server using the Retrofit Library. I write this entire documents in details with simple word.

How to send object parameter in Retrofit GET request?

I have a back-end server that works like this
"api/videos?search_object={"cat_id" :2, "channel_id" : 3, etc}
Basily you can give a search object as input and it will filter the list base on that object. Now I want to use this service with Retrofit with something like this
#GET("videos")
Call<VideoListResponse> listVideos(#Query("search_object") VideoSearchObject videoSearchObject);
But the above code doesn't work, I can first convert VideoSearchModel to JSON string that pass it to retrofit like this
#GET("videos")
Call<VideoListResponse> listVideos(#Query("search_object") String jsonString);
I wonder if there is a better more clear way? Any suggestions will be appreciated.
Retrofit 2 supports it. All you have to do is implementing a custom converter factory with the stringConverter() method overridden.
Consider the following Retrofit-friendly interface with a custom annotation:
#Target(PARAMETER)
#Retention(RUNTIME)
#interface ToJson {
}
interface IService {
#GET("api/videos")
Call<Void> get(
#ToJson #Query("X") Map<String, Object> request
);
}
The annotation is used to denote an argument that must be converted to a string.
Mock OkHttpClient to always respond with "HTTP 200 OK" and dump request URLs:
private static final OkHttpClient mockHttpClient = new OkHttpClient.Builder()
.addInterceptor(chain -> {
System.out.println(chain.request().url());
return new Response.Builder()
.request(chain.request())
.protocol(HTTP_1_0)
.code(HTTP_OK)
.body(ResponseBody.create(MediaType.parse("application/json"), "OK"))
.build();
})
.build();
private static final Gson gson = new Gson();
private static final Retrofit retrofit = new Retrofit.Builder()
.client(mockHttpClient)
.baseUrl("http://whatever")
.addConverterFactory(new Converter.Factory() {
#Override
public Converter<?, String> stringConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
if ( !hasToJson(annotations) ) {
return super.stringConverter(type, annotations, retrofit);
}
return value -> gson.toJson(value, type);
}
private boolean hasToJson(final Annotation[] annotations) {
for ( final Annotation annotation : annotations ) {
if ( annotation instanceof ToJson ) {
return true;
}
}
return false;
}
})
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
To test it you can simply invoke the service interface method:
final IService service = retrofit.create(IService.class);
service.get(ImmutableMap.of("k1", "v1", "k2", "v2")).execute();
Result:
http://whatever/api/videos?X={%22k1%22:%22v1%22,%22k2%22:%22v2%22}
Where the X parameter argument is an encoded representation of {"k1":"v1","k2":"v2"}.
You can try the below code, it works for me
#GET("api")
Call<Response> method(#Query("") JSONObject param);
Map<String, String> map = new HashMap<>();
map.put("key", "value");
JSONObject json = new JSONObject(map);
"api/videos?search_object={"cat_id" :2, "channel_id" : 3, etc}
Basically you can give a search object as input
No, you do not give an object as input. You provide multiple parameters enclosed in { } so that it looks like an object (a JavaScript object that is, not a Java object). In reality it is just a string.
The constructed url is just a bunch of characters. There is no such thing as an "object" in an url.
Keep doing it like #Query("search_object") String jsonString. Although you might also want to rename the parameter from jsonString to searchString, since that is what it is. It is not a JSON string. A JSON string would have all " characters escaped like \".

Retrofit Adding tag to the original request object

I'm trying to solve a problem where I'll be making a couple of asynchronous calls and based on the original request, I'm performing a task. To solve this issue, I'm trying to add a TAG to each request and then on successful response, I can get the tag and take action based on the tag. Here, I'm using TAG only to identify the original request.
Problem
Before calling the enqueue method, I'm setting the tag to the original request. But when I get the response in the successful callback, I'm getting different tag that I didn't set. Somehow the request object itself is coming as the tag object there. I'm not sure, how???
Please check the code below-
GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
final Call<List<Contributor>> call = gitHubService.repoContributors("square", "retrofit");
// Set the string tag to the original request object.
call.request().newBuilder().tag("hello").build();
call.enqueue(new Callback<List<Contributor>>() {
#Override
public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
Log.d("tag", response.raw().request().tag().toString());
// I'm getting Request{method=GET, url=https://api.github.com/repos/square/retrofit/contributors, tag=null} as the value of the tag. WHY????
final TextView textView = (TextView) findViewById(R.id.textView);
textView.setText(response.body().toString());
}
#Override
public void onFailure(Call<List<Contributor>> call, Throwable t) {
final TextView textView = (TextView) findViewById(R.id.textView);
textView.setText("Something went wrong: " + t.getMessage());
}
});
Can somebody point out that what exactly I'm doing wrong here. Any help would be appreciated.
For me this code is working
val CLIENT: OkHttpClient = OkHttpClient.Builder().apply {
addInterceptor(TagInterceptor())
}.build()
val SERVER_API: ServerApi = Retrofit.Builder()
.client(CLIENT)
.baseUrl(BASE_URL)
.build()
.create(ServerApi::class.java)
interface ServerApi {
#GET("api/notifications")
#Tag("notifications")
suspend fun getNotifications(): ResponseBody
}
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#Retention(AnnotationRetention.RUNTIME)
annotation class Tag(val value: String)
internal class TagInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val builder = request.newBuilder()
request.tag(Invocation::class.java)?.let {
it.method().getAnnotation(Tag::class.java)?.let { tag ->
builder.tag(tag.value)
}
}
return chain.proceed(builder.build())
}
}
Then cancel by tag
fun OkHttpClient.cancelAll(tag: String) {
for (call in dispatcher().queuedCalls()) {
if (tag == call.request().tag()) {
call.cancel()
}
}
for (call in dispatcher().runningCalls()) {
if (tag == call.request().tag()) {
call.cancel()
}
}
}
CLIENT.cancelAll("notifications")
This solution is clearly a hack, but it works.
Let's say you create your Retrofit service like this :
public <S> S createService(Class<S> serviceClass) {
// Could be a simple "new"
Retrofit.Builder retrofitBuilder = getRetrofitBuilder(baseUrl);
// Could be a simple "new"
OkHttpClient.Builder httpClientBuilder = getOkHttpClientBuilder();
// Build your OkHttp client
OkHttpClient httpClient = httpClientBuilder.build();
Retrofit retrofit = retrofitBuilder.client(httpClient).build();
return retrofit.create(serviceClass);
}
You will need to add a new CallFactory to your Retrofit instance, so it adds a tag every-time. Since the tag will be read-only, we will use an array of Object containing only one element, which you will be able to change later on.
Retrofit retrofit = retrofitBuilder.client(httpClient).callFactory(new Call.Factory() {
#Override
public Call newCall(Request request) {
request = request.newBuilder().tag(new Object[]{null}).build();
Call call = httpClient.newCall(request);
// We set the element to the call, to (at least) keep some consistency
// If you want to only have Strings, create a String array and put the default value to null;
((Object[])request.tag())[0] = call;
return call;
}
}).build();
Now, after creating your call, you will be able to change the contents of your tag:
((Object[])call.request().tag())[0] = "hello";
The request already have tag on it . You can get it form this code:
val invocation: Invocation? = call.request().tag(Invocation::class.java)
if (invocation != null) {
Timber.d("tag--${invocation.method().name}}-------${invocation.arguments()}")
}

How to create a retrofit.Response object during Unit Testing with Retrofit 2

While using RxJava and Retrofit 2 I am trying to create Unit Tests to cover when my app receives specific responses.
The issue I have is that with Retrofit 2 I cannot see a nice way of creating a retrofit.Response object without the use of reflection.
#Test
public void testLogin_throwsLoginBadRequestExceptionWhen403Error() {
Request.Builder requestBuilder = new Request.Builder();
requestBuilder.get();
requestBuilder.url("http://localhost");
Response.Builder responseBuilder = new Response.Builder();
responseBuilder.code(403);
responseBuilder.protocol(Protocol.HTTP_1_1);
responseBuilder.body(ResponseBody.create(MediaType.parse("application/json"), "{\"key\":[\"somestuff\"]}"));
responseBuilder.request(requestBuilder.build());
retrofit.Response<LoginResponse> aResponse = null;
try {
Constructor<retrofit.Response> constructor= (Constructor<retrofit.Response>) retrofit.Response.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
aResponse = constructor.newInstance(responseBuilder.build(), null, null);
} catch (Exception ex) {
//reflection error
}
doReturn(Observable.just(aResponse)).when(mockLoginAPI).login(anyObject());
TestSubscriber testSubscriber = new TestSubscriber();
loginAPIService.login(loginRequest).subscribe(testSubscriber);
Throwable resultError = (Throwable) testSubscriber.getOnErrorEvents().get(0);
assertTrue(resultError instanceof LoginBadRequestException);
}
I have tried using the following but this creates an OkHttp Response vs a Retrofit Response.
Request.Builder requestBuilder = new Request.Builder();
requestBuilder.get();
requestBuilder.url("http://localhost");
Response.Builder responseBuilder = new Response.Builder();
responseBuilder.code(403);
responseBuilder.protocol(Protocol.HTTP_1_1);
The retrofit.Response class has static factory methods to create instances:
public static <T> Response<T> success(T body) {
/* ... */
}
public static <T> Response<T> success(T body, com.squareup.okhttp.Response rawResponse) {
/* ... */
}
public static <T> Response<T> error(int code, ResponseBody body) {
/* ... */
}
public static <T> Response<T> error(ResponseBody body, com.squareup.okhttp.Response rawResponse) {
/* ... */
}
For example:
Account account = ...;
retrofit.Response<Account> aResponse = retrofit.Response.success(account);
Or:
retrofit.Response<Account> aResponse = retrofit.Response.error(
403,
ResponseBody.create(
MediaType.parse("application/json"),
"{\"key\":[\"somestuff\"]}"
)
);
Note: In latest Retrofit version (2.7.1) for Kotlin, it recommends to use extension method like this:
Response.error(
400,
"{\"key\":[\"somestuff\"]}"
.toResponseBody("application/json".toMediaTypeOrNull())
)
This falls under Effective Java Item 1: Consider static factory methods instead of constructors.
Heres how to mock just the retrofit responses
First you need to add these dependencies in build.gradle:
// mock websever for testing retrofit responses
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
Mock a successful 200 response:
val mockResponseBody = Mockito.mock(MoviesResponse::class.java)
val mockResponse = Response.success(mockResponseBody)
Mock an unsuccessful response (eg 400, 401, 404):
val errorResponse =
"{\n" +
" \"type\": \"error\",\n" +
" \"message\": \"What you were looking for isn't here.\"\n"
+ "}"
val errorResponseBody = errorResponse.toResponseBody("application/json".toMediaTypeOrNull())
val mockResponse = Response.error<String>(400, errorResponseBody)
No need to create a mock webserver and all that extra work.
A Kotlin + Mockito + okhttp3 example using Response.Builder
val mockResponse: Response<MyResponseReturnType> =
Response.success(mock<MyResponseReturnType>(),
okhttp3.Response.Builder()
.code(200)
.message("Response.success()")
.protocol(Protocol.HTTP_1_1)
.request(Request.Builder().url("http://test-url/").build())
.receivedResponseAtMillis(1619053449513)
.sentRequestAtMillis(1619053443814)
.build())
Instead of using the toResponseBody() extension function on a String, I used it on a Byte Array like shown below.
Response.error(400, byteArrayOf().toResponseBody())
Doing byteArrayOf().toResponseBody() does create a ResponseBody.

Categories

Resources