Tiempo de lectura: 9 minutos

Test de integración con Espresso, Dagger2, OkHttp3, MockWebServer y DaggerMock (sin test dobles).

Un test de integración es una prueba donde se verifica el correcto funcionamiento entre varios módulos de nuestra aplicación. El número de módulos que se pueden abarcar depende de la granularidad que queramos dar al test.

La prueba que vamos a ver en este post podría ser un test de aceptación pero en este caso usaremos un mock para la API por tanto no podemos decir que lo sea porque una parte de la prueba no es real.

El test se hará usando sus clases reales excepto la parte del servidor. Creo que esto puede ser muy útil para hacer pruebas de integración sin depender del estado del servidor externo.

Instalación de librerías.

Las librerías que vamos a usar en este post se pueden instalar a través de Gradle.

  • Espresso (sustituir $supportLibreryVersion por la versión actual):
  • MockWebServer
  • DaggerMock
  • OkHttp3 + IdlingResource
  • Dagger2
    •  

Espresso.

Espresso es la librería que nos ofrece Google para testear las interfaces gráficas de las aplicaciones. Con esta librería es muy sencillo interactuar con las vistas.

Dagger2.

Para el desarrollo de la aplicación se ha usado el inyector de dependencias Dagger2, el más usado en Android. Como queremos usar un mock que sustituya nuestro servidor y por tanto la API a la que atacamos, necesitamos obtener ciertos objetos para modificar sus valores. Esta modificación es sencilla gracias al uso de la inyección de dependencias.

Dagger2 necesita que definamos unos módulos donde proveamos los objetos que estamos inyectando. Cuando se inyecta una clase, Dagger2 busca resolver todas las dependencias de esa clase: a través de los métodos definidos en los módulos o a través de los constructores o métodos con la anotación @Inject.

Para poder realizar nuestros test necesitamos inyectarlos con Dagger2. Si el código de la inyección de los test lo hiciéramos en el código de la aplicación, estaríamos metiendo funcionalidades que sólo se usarían para hacer testing y esto no es recomendable.

Otra opción que existe es crear nuevos componentes y módulos expresamente para nuestros test y que extiendan y sobrescriban a los que usamos es nuestra aplicación. Podemos ver un post muy bueno sobre esto en Medium. Esta opción es la que se suele hacer habitualmente, pero personalmente no me gusta porque puede ser un trabajo bastante tedioso y puede conseguir que por “pereza” a preparar el test se deje de hacer.

En este post vamos a ver una forma sencilla de hacer tests de integración con DaggerMock y MockWebServer con sólo una línea de código.

Cliente OkHttp3

En el ejemplo se ha usado Retrofit2 con el cliente OkHttp3. Aunque para poder usar MockWebServer no es necesario que se use OkHttp3 tenemos que hacer referencia a esta librería porque necesitamos configurarla para que pueda usar nuestro servidor mock. Si usas otro cliente, sólo tendrás que modificar los datos que se detallan aquí. La única diferencia será la forma de hacerlo.

OkHtttp3 trabaja con interceptores que es una clase que implementa una interfaz y que se ejecuta cuando se inicia el cliente. Los interceptores son una forma sencilla de modificar el comportamiento y valores del cliente. 

Otro beneficio que tenemos al usar Okhttp3 es que la empresa desarrolladora de la librería Square ha facilitado a través de Jake Wharton un Idling Resource que nos va a permitir ejecutar el test, realizar la llamada, bloquear el test y reanudarlo cuando se reciba la respuesta.

DaggerMock

DaggerMock es una librería que nos ahorra todo el trabajo de tener que crear los módulos sólo para testing.. En una sólo la línea de código y a través de una Regla (@Rule) de jUnit podemos tener acceso a todas las dependencias del test que se crean a través de Dagger.

MockWebServer

MokWebServer es una librería que simula un servidor en nuestro dispositivo móvil. Con esta librería podemos simular la API a la que tenemos que atacar. 

El funcionamiento es muy sencillo. En el inicio del test le indicamos al MockWebServer qué nos tiene que devolver (ej: un json) y cuando hagamos una llamada a la API real, será MockWebServer quién recoja esa petición y nos devuelva el json que le hemos establecido previamente. Para que esto sea posible, tenemos que configurar nuestro cliente http para que las peticiones se redirijan a nuestro servidor mock.

Código del Test

Contexto: Vamos a testear el login de una aplicación que tengo en GitHub para este tipo de pruebas. Puedes encontrarla aquí: Foodie.

Necesitamos crearnos un test de la actividad LoginActivity llamado LoginActivityTest. Dentro de la clase, vemos la declaración de un objeto muy interesante: LoginActivityPageObject. 

PageObject

PageObject es un término creado por Martín Fowler que viene a decir que en estas clases escribamos código que nos permita manejar u obtener elementos de la parte gráfica y evitar duplicar código en el test. Tiene una regla básica y es que las acciones descritas en este objeto tenga acciones cercanas a las que podría hacer el usuario. Ej: writeUserName(“Chema”).

En el ejemplo podrás nos hemos creado un PageObject: LoginPageObject.

Volvamos al test y veamos el código completo del test.

Vayamos analizando las partes importantes:

Es la regla para poder usar DaggerMock. Esto nos permitirá poder inyectar nuestro test con Dagger sin tener que crearnos módulos exclusivos para las pruebas.

La clase EspressoDaggerMockRule es sencilla:

Pasamos a configurar el servidor Mock

Por defecto se le pasa como url “/” y se inicia. 

Ahora configuraremos el cliente http con los datos de MockWebServer para que nuestro cliente use este servidor.

La anotación @InjectFromComponent no permite obtener una instancia del objeto en nuestro test.

Tenemos tres partes:

  1. Obtenemos una instancia de nuestro cliente con la anotación @InjectFromComponent
  2. Obtenemos una instancia de un Interceptor que se ha añadido a nuestro cliente en el módulo NetworkModule. Este interceptor nos va a permitir cambiar ciertos datos de configuración del cliente.
  3. Cambiamos los datos del cliente que usaría nuestra aplicación por los datos del MockWebServer. Se necesitaría el protocolo, el puerto y el hostname. El protocolo se necesita por si se cambia de https a http.

Con esto ya tendríamos nuestro cliente preparado para usar nuestro servidor mock.

Cuando ejecutamos un test y hacemos una petición a un servicio, tenemos el problema que normalmente se pasa del hilo principal (UI) a un hilo secundario. Para ‘bloquear’ nuestro test hasta que se reciba la respuesta de un proceso en background, Espresso nos ofrece la interfaz IdlingResource. Además, Jake Wharton nos facilita la clase ya realizada para el cliente OkHttp3. Solo tendremos que añadirlo a nuestro fichero Gradle para que se descargue y podamos usarla. 

Para usar las clases IdlingResource hay que ‘registrarlas’ en Espresso. Veamos como:

Los pasos a seguir son sencillos:

  1. Necesitamos a través de DaggerMock el cliente que se usa en la aplicación.
  2. Con el OkHttp3IdlingResource.create() creamos el IdlingResource. Le tenemos que pasar el cliente que se ha obtenido en el punto 1.
  3. Tenemos que registrarlo y quitarlo por cada test. Esto lo hacemos con los métodos que nos facilita Espresso .registerIdlingResource y unregisterIdlingResource.

Con todo esto, tendríamos preparado nuestro test para usarlo con MockWebServer. Veamos un ejemplo de un test:

El test que se ha definido ejecuta todo el código real de nuestra aplicación excepto la parte de la petición al servidor, que se haría con nuestro MockWebServer.

Con las líneas:

Lo que se hace es añadir un string en formato json al MockWebServer emulando la respuesta que nos daría el servidor real si las credenciales del login fueran correctas.

Aquí se puede ver cómo creo la respuesta que quiero que me de el servidor MockWebServer

 

Podéis ver un ejemplo completo en el repositorio GitHub: FoodieApp.

Referencias: