Android Mock API Server

Telefónica Engineering
Telefonica Engineering
3 min readJan 30, 2024

--

Pablo García Núñez

Photo by Adi Goldstein on Unsplash

As an Android developer, it is a common challenge to initiate the development of an app or a feature without access to a fully released backend. This scenario often poses obstacles to the development process and heightens the potential for issues when it comes time to deploy.

To address this, we have created a library built upon Mock-Web-Server: Android Mock Api Server allowing us to effortlessly simulate the responses that a fully operational backend would deliver. This innovative approach simplifies testing across the view, domain, and data layers, enabling us to identify and rectify potential issues with ease. One of the advantages that the library provides is simplicity when it comes to using it, as it employs a language very similar to what we can find in testing libraries like Mockito.

Initial Usage Example

First, let’s examine an illustrative example to understand the simple nature of using the library.

Suppose we are in the process of developing an employee management application, and we have prior knowledge of the response structure we expect when querying for a list of employees. The expected JSON response would be as follows:

employees.json

[
{
"id": "id_01",
"name": "Paul",
"surname": "Smith",
"age": 25,
"position": "Engineer"
},
{
... // More employees
}
]

Now, all that remains is to enqueue the responses for each specific path request, defining the precise response we intend to receive.

class EnqueueMockResponses @Inject constructor(
private val mockHelper: MockHelper
) {

fun enqueueEmployeeList() {
mockHelper.enqueue {
whenever(
path = "/our_endpoint_route/employees",
method = Method.Get
).thenReturnFromFile(
pathFromFile = "employees.json",
httpResponseCode = 200,
delayInMillis = 2000L
)
}
}
}

Let’s explore the internal workings of the library.

First Step: Setup the library

As previously mentioned, this library is built atop MockWebServer, leveraging its capabilities to simulate server responses through carefully configured adjustments.

To commence, the initiation of the MockWebServer server involves specifying a port number, a task seamlessly handled by our provided setup method.

fun setUp(
port: Int = 0,
enableSsl: Boolean = false,
) {
mockApiClient.setUp(enableSsl = enableSsl)
mockApiClient.startServer(port)
}

In instances where the port is left unspecified, the server dynamically selects an available port during initialization.

Once the server is initialized, configuring our HTTP client becomes straightforward. A crucial step involves informing the client of the base URL for our requests, which is obtained from the newly launched server

suspend fun getBaseUrl(): String = mockApiClient.getBaseUrl()

Second Step: Provide mock responses

Now, it’s time to tell our library on the responses we desire based on the various calls our application may make. To facilitate this, we have crafted an intuitive and user-friendly DSL, reminiscent of the simplicity found in Mockito.

As highlighted at the beginning of this post, this approach provides us with an instinctive method for mocking responses, streamlining the process

fun enqueue(block: EnqueuingContext.() -> Unit) = block(EnqueuingContext(this))
class EnqueuingContext(val mockHelper: MockHelper) {
fun whenever(
path: Path,
method: Method = Method.Get
): MockResponseBuilderWithRequestInfo = MockResponseBuilderWithRequestInfo(mockHelper, RequestInfo(path, method))
}
class MockResponseBuilderWithRequestInfo(
private val mockHelper: MockHelper,
private val requestInfo: RequestInfo,
) {
fun thenReturnFromFile(
pathFromFile: String,
httpResponseCode: Int = MockedResponse.DEFAULT_MOCK_HTTP_RESPONSE_CODE,
delayInMillis: Long = MockedResponse.DEFAULT_MOCK_DELAY_IN_MILLIS,
) {
mockHelper.mockApiClient.enqueue(
requestInfo = requestInfo,
mockedResponse = MockedApiResponse(
body = mockHelper.fileReader.readJsonFile(pathFromFile) ?: MockedApiResponse.DEFAULT_BODY,
httpResponseCode = httpResponseCode,
delayInMillis = delayInMillis,
)
)
}
}

The matching of specified paths is done using Regex; this way, we enable the mocking of requests with dynamic values.

fun enqueueEmployeeList() {
mockHelper.enqueue {
whenever(
path = "api/.*/our_endpoint_route?employee_id=.*",
method = Method.Get
).thenReturnFromFile(
pathFromFile = "employee_detail.json",
httpResponseCode = 200,
delayInMillis = 2000L
)
}
}

Previous mock match with next example urls:

https://localhost:8080//api/1.0.0/our_endpoint_route?employee_id=001
https://localhost:8080//api/1.0.1/our_endpoint_route?employee_id=002
...

Third Step: Create JSON responses

In the third and final step, it is imperative to generate the JSON responses that our mock server will deliver. This is achieved by creating JSON files containing the requisite response bodies, which are then stored in the assets folder.

As emphasized earlier in this post, given that this library is designed to facilitate application development, we recommend housing these files in the debug folder. This approach ensures that the production version remains unencumbered by unnecessary file size increases.

Our Android Mock Api Server Library

https://github.com/Telefonica/android-mock-api-server is an open-source library developed by us.

We welcome and appreciate any suggestions or enhancements you may have. Your valuable input contributes to the ongoing refinement and improvement of our library.

--

--