Android Retrofit Mock Response Tutorial
Writing test cases is a difficult task when handling of different server responses. Testing error responses can be quite problematic and your app might not cover all the different scenarios. In order to test your Android apps, one thing that normally gets frequently overlooked is the apps ability to handle different server responses. We can make the client return different responses which are stored in .JSON files within the test project. In this example, I will discuss on implementing Android retrofit mock response by custom Retrofit Client to mock HTTP response codes.
Creating a New Project
1. Open Android Studio IDE in your computer.
2. Create a new project and Edit the Application name to “RetrofitMockResponseExample”.
(Optional) You can edit the company domain or select the suitable location for current project tutorial. Then click next button to proceed.
3. Select Minimum SDK (API 15:Android 4.0.3 (IceCreamSandwich). I choose the API 15 because many Android devices currently support more than API 15. Click Next button.
4. Choose “Empty Activity” and Click Next button
5. Lastly, press finish button.
Add new dependency
Go to the build.gradle file and add the retrofit dependency in your project.
compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.google.code.gson:gson:2.7' compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
Edit Android.Manifest.xml
Open Android Manifest file and Insert internet permission in order to call API.
<uses-permission android:name="android.permission.INTERNET" />
Add an assets folder
To create a new assets folder, you can refer to the following image.
Add a new .json file
Create folder “mock.api” in the assets folder and create another folder “user” in the “mock.api” folder. After that, create a new JSON file “login” and copy the source code below.
{ "status": "success", "data": { "username": "abc", "email": "abc@gmail.com" }, "message": "" }
Create fake interceptor java class
Create a new class “FakeInterceptor” and implements Interceptor on it. The API response will make force to use the .json file from the assets.
import android.content.Context; import android.util.Log; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.util.ArrayList; import java.util.List; import okhttp3.Interceptor; import okhttp3.MediaType; import okhttp3.Protocol; import okhttp3.Response; import okhttp3.ResponseBody; /** * */ public class FakeInterceptor implements Interceptor { private static final String TAG = FakeInterceptor.class.getSimpleName(); private static final String FILE_EXTENSION = ".json"; private Context mContext; private String mContentType = "application/json"; public FakeInterceptor(Context context) { mContext = context; } /** * Set content type for header * @param contentType Content type * @return FakeInterceptor */ public FakeInterceptor setContentType(String contentType) { mContentType = contentType; return this; } @Override public Response intercept(Chain chain) throws IOException { List<String> listSuggestionFileName = new ArrayList<>(); String method = chain.request().method().toLowerCase(); Response response = null; // Get Request URI. final URI uri = chain.request().url().uri(); Log.d(TAG, "--> Request url: [" + method.toUpperCase() + "]" + uri.toString()); String defaultFileName = getFileName(chain); //create file name with http method //eg: getLogin.json listSuggestionFileName.add(method + upCaseFirstLetter(defaultFileName)); //eg: login.json listSuggestionFileName.add(defaultFileName); String responseFileName = getFirstFileNameExist(listSuggestionFileName, uri); if (responseFileName != null) { String fileName = getFilePath(uri, responseFileName); Log.d(TAG, "Read data from file: " + fileName); try { InputStream is = mContext.getAssets().open(fileName); BufferedReader r = new BufferedReader(new InputStreamReader(is)); StringBuilder responseStringBuilder = new StringBuilder(); String line; while ((line = r.readLine()) != null) { responseStringBuilder.append(line).append('\n'); } Log.d(TAG, "Response: " + responseStringBuilder.toString()); response = new Response.Builder() .code(200) .message(responseStringBuilder.toString()) .request(chain.request()) .protocol(Protocol.HTTP_1_0) .body(ResponseBody.create(MediaType.parse(mContentType), responseStringBuilder.toString().getBytes())) .addHeader("content-type", mContentType) .build(); } catch (IOException e) { Log.e(TAG, e.getMessage(), e); } } else { for (String file : listSuggestionFileName) { Log.e(TAG, "File not exist: " + getFilePath(uri, file)); } response = chain.proceed(chain.request()); } Log.d(TAG, "<-- END [" + method.toUpperCase() + "]" + uri.toString()); return response; } private String upCaseFirstLetter(String str) { return str.substring(0, 1).toUpperCase() + str.substring(1); } private String getFirstFileNameExist(List<String> inputFileNames, URI uri) throws IOException { String mockDataPath = uri.getHost() + uri.getPath(); mockDataPath = mockDataPath.substring(0, mockDataPath.lastIndexOf('/')); Log.d(TAG, "Scan files in: " + mockDataPath); //List all files in folder String[] files = mContext.getAssets().list(mockDataPath); for (String fileName : inputFileNames) { if (fileName != null) { for (String file : files) { if (fileName.equals(file)) { return fileName; } } } } return null; } private String getFileName(Chain chain) { String fileName = chain.request().url().pathSegments().get(chain.request().url().pathSegments().size() - 1); return fileName.isEmpty() ? "index" + FILE_EXTENSION : fileName + FILE_EXTENSION; } private String getFilePath(URI uri, String fileName) { String path; if (uri.getPath().lastIndexOf('/') != uri.getPath().length() - 1) { path = uri.getPath().substring(0, uri.getPath().lastIndexOf('/') + 1); } else { path = uri.getPath(); } return uri.getHost() + path + fileName; } }
Add a new package folder “models”
Right-click your package name and create a new package folder “models” for a retrofit response.
Create java class for Response
Create class “ResponseStatus” in the “models” package folder, this is getting the status and message of the response.
class ResponseStatus { private String status; private String message; public String getStatus() { return status; } public String getMessage() { return message; } }
In the “models” package folder, create another class “ResponseData” is to get the JSON response data from this object. It will extend the “ResponseStatus” class.
import com.google.gson.JsonElement; public final class ResponseData extends ResponseStatus { private JsonElement data; public JsonElement getData() { return data; } }
Add another new package folder “api”
Right-click your package name and create a new package folder “api” for a retrofit request.
Create a new java class
Right-click the api folder and create a new class “RestClient”. After that insert the following code to this class. This will add the FakeInterceptor in order to use the JSON file from the assets.
import android.content.Context; import com.questdot.retrofitmockresponseexample.FakeInterceptor; import okhttp3.OkHttpClient; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; /** * */ public final class RestClient { private static RestService mRestService = null; public static RestService getClient(Context context) { if (mRestService == null) { final OkHttpClient client = new OkHttpClient .Builder() .addInterceptor(new FakeInterceptor(context)) .build(); final Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .baseUrl("//mock.api") .client(client) .build(); mRestService = retrofit.create(RestService.class); } return mRestService; } }
Create a new interface class
In the API folder, create an interface “RestService” so that you able to call the login API according to the query and path.
import com.questdot.retrofitmockresponseexample.models.ResponseData; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Query; public interface RestService { @GET("/user/login") Call<ResponseData> login(@Query("username") final String id, @Query("pwd") final String pwd); }
Edit activity_main.xml layout
Go to this activity_main.xml file and it will add an id in your textview.
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="//schemas.android.com/apk/res/android" xmlns:app="//schemas.android.com/apk/res-auto" xmlns:tools="//schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.questdot.retrofitmockresponseexample.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:id="@+id/txtResponse" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout>
Edit MainActivity.java class
Open MainActivity.java class and this class will be called the login API and display it on the screen.
import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.TextView; import com.questdot.retrofitmockresponseexample.api.RestClient; import com.questdot.retrofitmockresponseexample.models.ResponseData; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class MainActivity extends AppCompatActivity { TextView txtResponse; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txtResponse = (TextView)findViewById(R.id.txtResponse); RestClient.getClient(this).login("abc", "123456").enqueue(new Callback<ResponseData>() { @Override public void onResponse(Call<ResponseData> call, Response<ResponseData> response) { txtResponse.setText(response.body().getData().toString()); Log.d("MainActivity", response.body().getData().toString()); } @Override public void onFailure(Call<ResponseData> call, Throwable t) { } }); } }
Run Your Project
Lastly, you can now run your android project and view the mock json response from your devices.
hi brother
Can i get the source code for this example
Hi! if i call xxx from different activities will may to produce crashes for context problems?
Hi! if i call RestClient.getClient(context) from different activities will may to produce crashes for context problems?