Distrito Telefónica. Hub de Innovación y Talento

Volver
Development

Servidor API simulado: Pruebas fáciles para desarrollo Android

Como desarrollador de Android, es un reto común iniciar el desarrollo de una aplicación o una función sin acceso a un backend completamente liberado. Este escenario suele plantear obstáculos al proceso de desarrollo y aumenta las posibilidades de que surjan problemas a la hora de la implantación. Para solucionarlo, hemos creado una biblioteca basada en Mock-Web-Server: Servidor Android Mock Api que nos permite simular sin esfuerzo las respuestas que daría un backend plenamente operativo. Este enfoque innovador simplifica las pruebas en las capas de vistas, dominios y datos, lo que nos permite identificar y rectificar posibles problemas con facilidad. Una de las ventajas que aporta la biblioteca es la sencillez a la hora de utilizarla, ya que emplea un lenguaje muy similar al que podemos encontrar en bibliotecas de prueba como Mockito.

Ejemplo de uso inicial

En primer lugar, examinemos un ejemplo ilustrativo para comprender la sencillez de uso de la biblioteca. Supongamos que estamos desarrollando una aplicación de gestión de empleados y que conocemos la estructura de respuesta que esperamos al consultar una lista de los mismos. La respuesta esperada de JSON sería la siguiente:
employees.json
[
  {
    "id": "id_01",
    "name": "Paul",
    "surname": "Smith",
    "age": 25,
    "position": "Engineer"
  },
  {
    ... // More employees
  }
]
Ahora, todo lo que queda es poner en cola las respuestas para cada solicitud de ruta específica, definiendo la respuesta precisa que pretendemos recibir.
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
      )
    }
  }
}
Exploremos el funcionamiento interno de la biblioteca.

Primer paso: Configurar la biblioteca

Como se mencionó anteriormente, esta biblioteca está construida sobre MockWebServer, aprovechando sus capacidades para simular las respuestas del servidor a través de ajustes cuidadosamente configurados. Para empezar, la iniciación del servidor MockWebServer implica la especificación de un número de puerto, una tarea gestionada sin problemas por nuestro método proporcionado de configuración.
fun setUp(
    port: Int = 0,
    enableSsl: Boolean = false,
) {
    mockApiClient.setUp(enableSsl = enableSsl)
    mockApiClient.startServer(port)
}
En los casos en los que no se especifica el puerto, el servidor selecciona dinámicamente un puerto disponible durante la inicialización. Una vez inicializado el servidor, la configuración de nuestro cliente HTTP resulta sencilla. Un paso fundamental consiste en informar al cliente de la URL base para nuestras solicitudes, que se obtiene del servidor recién lanzado.
suspend fun getBaseUrl(): String = mockApiClient.getBaseUrl()

Segundo paso: Proporcionar respuestas simuladas

Ahora, es el momento de indicar a nuestra biblioteca las respuestas que deseamos en función de las distintas llamadas que pueda realizar nuestra aplicación. Para facilitarlo, hemos creado un DSL intuitivo y fácil de usar, que recuerda a la sencillez de Mockito. Como se destacó al principio de este post, este enfoque nos proporciona un método instintivo para burlar las respuestas, agilizando el proceso.
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,
            )
        )
    }
}
La coincidencia de las rutas especificadas se realiza utilizando Regex; de esta forma, permitimos la imitación de solicitudes con valores dinámicos.
fun enqueueEmployeeList() {
    mockHelper.enqueue {
      whenever(
        path = "api/.*/our_endpoint_route?employee_id=.*",
        method = Method.Get
      ).thenReturnFromFile(
        pathFromFile = "employee_detail.json",
        httpResponseCode = 200,
        delayInMillis = 2000L
      )
    }
  }
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

Tercer paso: Crear respuestas JSON

En el tercer y último paso, es imprescindible generar las respuestas JSON que entregará nuestro servidor simulado. Para ello, se crean archivos JSON con los cuerpos de respuesta necesarios, que se almacenan en la carpeta de activos. Como se ha destacado anteriormente en este post, dado que esta biblioteca está diseñada para facilitar el desarrollo de aplicaciones, se recomienda alojar estos archivos en la carpeta de depuración. Este enfoque garantiza que la versión de producción no se vea afectada por aumentos innecesarios del tamaño de los archivos.

Nuestra biblioteca de servidores Mock Api para Android

Android-mock-api-server es una biblioteca de fuente abierta desarrollada por nosotros. Agradecemos cualquier sugerencia o mejora. Tus valiosas aportaciones contribuyen al continuo perfeccionamiento y mejora de nuestra biblioteca.