With this snippet you can easily and quickly mock an API during developmentby placing JSON files in your res/raw/
folder. This comes in handy when the actual
API does not exist yet.
For mocking an API for unit tests, have a look at Retrofit’s MockService (GitHub).
Retrofit
When creating your Retrofit instance, you can add a MockIntercepter
which transforms each request into reading from res/raw/
instead:
val mockInterceptor = MockInterceptor()
mockClient = OkHttpClient.Builder()
.addInterceptor(mockInterceptor)
.build()
In your Retrofit instance, use this client instead of the regular client. For example:
private val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.ROOT_URL_PRODUCTION)
.client(if (BuildConfig.DEBUG) mockClient else okHttpClient)
.addConverterFactory(MoshiConverterFactory.create())
.build()
Mock Interceptor
The Mock Interceptor transforms the requests to load the response from a file:
class MockInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val url = chain.request().url()
val path = "res/raw/${url.getLastPathSegment()}"
val response = path.readFile(this)
require(!response.isNullOrEmpty()) { "JSON file $path should exist and not be empty" }
return Response.Builder()
.code(200)
.message(response)
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.body(ResponseBody.create(MediaType.parse("application/json"), response))
.addHeader("content-type", "application/json")
.build()
}
}
This assumes the following extension functions:
/**
* Opens an InputStream to the specified path.
* Pro tip: By prefixing `raw/` or `res/raw/` to the String you can load files from res/raw without a Context.
*/
fun String.openStream(clz: Any) = clz.javaClass.classLoader?.getResourceAsStream(this)
fun InputStream.readFile() = this.bufferedReader().use(BufferedReader::readText)
fun String.readFile(clz: Any) = this.openStream(clz)?.readFile()
/**
* Added a convenient shortcut here because Android's Uri class
* has a getLastPathSegment method but OkHttp does not.
*/
fun HttpUrl.getLastPathSegment(): String = this.pathSegments().last()
Api Service
Now, if your ApiService looks like this:
interface ApiService {
@GET("example.json")
suspend fun getExample(): ExampleResponse
}
The file will be loaded from res/raw/example.json
instead.