La guía definitiva para probar dobles en Android – Parte 2: Ejercicio | de Phellip Silva | mayo 2022

Contenidos

Mocks, stubs, fakes, dummies y spys en Android: de la teoría a la (buena) práctica

Foto de alireza irajinia en UnsplashEn la primera parte de esta serie, aprendimos la teoría sobre el uso de dobles de prueba. ¡Ahora veamos cómo usarlos en el mundo de Android con algunos ejemplos! En caso de que queramos crear dobles de prueba manualmente, no hay gran secreto en Android. Solo tiene que implementarlos correctamente de acuerdo con el contrato de dependencia real (como en la mayoría de las plataformas o idiomas) y reemplazar el empleado real por el falso. Las técnicas de inyección de dependencias nos pueden ayudar en esta tarea. Las diferencias con otras tecnologías comienzan a surgir cuando Estamos hablando de herramientas.. En el mundo Android podemos usar frameworks compatibles con los lenguajes Java y Kotlin para crear nuestros dobles. Los más conocidos incluyen Mockito (para Java) y MockK (para Kotlin). Aquí hay algunos ejemplos usando MockK:Configurar un stub con MockkConfigurar un simulacro con Mockk

costos dobles y de mantenimiento

Como se mencionó en el artículo anterior, todos los dobles de prueba se pueden configurar manualmente o mediante herramientas externas. Tenga en cuenta que las herramientas externas como MockK o Mockito para este propósito, aunque inicialmente son más fáciles de configurar, tienden a combinar nuestro código de prueba con los detalles de implementación de las dependencias. Explicación con más detalles. La teoría es que al probar un comportamiento, cualquier cambio que no cambie ese comportamiento (digamos un refactor) no debería romper el código de prueba, como cambiar el método fetchAllNotes() para obtener un nuevo parámetro debería romper el NoteRepository -Do no interrumpir la prueba… pero desafortunadamente en el ejemplo anterior se romperá, ya que la prueba comparte detalles de la implementación de NoteApi como parte de la configuración de MockK (línea 6). Si imagina varias pruebas que ejecutan la misma configuración en varias piezas de código, es posible que encuentre varias pruebas fallidas debido a una refactorización de clase, lo que agrega costos generales y de mantenimiento a cada refactorización. En la otra cara de la moneda, podemos encontrar los Dobles configurados manualmente, o Dobles que utilizan el mismo contrato que la clase real para su alineación. Tienen la desventaja de aumentar potencialmente el tamaño de la base de código inicialmente, pero también tienen la ventaja de no pasar los detalles de implementación a las pruebas, ya que no dependen de la herramienta mágica. Esto puede mejorar potencialmente la capacidad de mantenimiento a largo plazo. Aquí hay una comparación de cómo los diferentes tipos de configuraciones duales pueden afectar la capacidad de mantenimiento de su base de código:Comparación no científica de los costes de mantenimiento de las pruebas a medida que aumenta el tamaño de la base de código Según lo anterior para bases de código grandes, Mi recomendación es que no configures tus dobles con herramientas que te hagan divulgar detalles de implementación.. Esto es contrario a la intuición, ya que podría pensar que está reinventando la rueda al no usar estas herramientas, pero cada nuevo marco/biblioteca agregado a su proyecto implica una compensación, por lo que siempre es bueno saber cuáles son las implicaciones y cómo usar la herramienta de manera adecuada. Una cosa importante a mencionar es que no estoy en contra de las herramientas, estoy en contra de su mal uso, como se menciona en el siguiente tweet: Publicación de Rafa Araujo que describe el mal uso de MockK por parte de la comunidad de Android, según el escenario, usted También podría considerar el uso de la escuela de prueba de convivencia para reducir el código de prueba doble repetitivo. Como se describe en el siguiente tuit: publicación de Jake Wharton que menciona la escuela de prueba social y recomienda el uso de falsificaciones. En la siguiente sección, veremos algunas estrategias de duplicación de prueba que podrían usarse en su código base de Android, estas estrategias se pueden aplicar independientemente de la configuración de duplicación que elija (manualmente o mediante herramientas). En la base de la pirámide de testing encontramos las pruebas no instrumentadas, que son pruebas que no utilizan emuladores o dispositivos reales durante su ejecución. Se sabe que las pruebas de este grupo tienen un alto nivel de aislamiento y velocidad, Así que el uso de dobles será muy común aquí. Para demostrar el uso de dobles de prueba en el mundo de Android, veamos cómo se vería una aplicación en una arquitectura MVVM:Ejemplo de una arquitectura MVVM. En verde encontramos los componentes que suelen depender de la plataforma Android (View y Local Data Source) para ejecutar sus pruebas. En azul se muestran los componentes que normalmente no dependen de la plataforma Android (ViewModel, Repository y Remote Data Source) para ejecutar sus pruebas. En la mayoría de los escenarios, estará trabajando en un proyecto que ya usa (o abusa) de los dobles de prueba. Así que intentaré demostrar una estrategia de prueba que utiliza la escuela de prueba única y cómo podría mejorar su configuración actual:Ejemplo de uso de dobles de prueba con MVVM en la capa no instrumentada. En esta estrategia anterior, usamos el siguiente razonamiento:

probando la vista

Las pruebas de capa de vista no son muy comunes en la parte no instrumentada. Para que podamos construirlos, probablemente necesitemos construir una prueba roboeléctrica, que es notoriamente más lenta que las pruebas puras de Java/Kotlin. Si prueba sus puntos de vista, ya que es una prueba costosa, mi recomendación es que no usemos dobles para ViewModel sino para el repositorio. La razón de esto es que los ViewModels son (por diseño) componentes que están estrechamente acoplados a Views y, por lo tanto, el comportamiento de View estaría mejor representado si usáramos ViewModels reales. Además, no obtendríamos mucha ganancia de velocidad al usar un doble para ViewModel, esta prueba sería lenta de todos modos. En este ejemplo particular, el repositorio estaría bien reemplazado con un talón.

Probando el modelo de vista

Para probar la capa de ViewModel, podemos ejecutar una prueba de unidad sin Android y reemplazar el repositorio con uno Talón, directo al grano. Las dependencias de otros ViewModels también podrían reemplazarse por dobles de prueba.

Probando el repositorio

Para probar la capa del repositorio, también podemos ejecutar una prueba unitaria gratuita de Android. Aquí reemplazamos la fuente de datos local y la fuente de datos remota con talón o falsificaciones.

Prueba de la fuente de datos remota

Para probar la capa de fuente de datos remota, probablemente necesitemos reemplazar el backend con un doble. La forma más fácil sería crear uno. falso Utilizando la herramienta MockWebServer, que simula un servidor HTTP encargado de devolver respuestas preconfiguradas. Las pruebas en esta capa son útiles para garantizar que la serialización de la respuesta de back-end funcione correctamente.

Prueba de la fuente de datos local

Para probar la capa de fuente de datos local, probablemente necesitemos reemplazar la base de datos local con una falso o talón. Si queremos hacer este tipo de pruebas a nivel no instrumentado, Robolectric también podría ser una opción. Aún así, creo que esta forma de prueba podría ser más útil en la parte instrumentada de su conjunto de pruebas. Para probar la capa de datos de forma aislada, mejor hacerlo de forma más fiable. Tenga en cuenta que esto es solo un ejemplo, si tiene una estrategia de prueba doble bien estructurada y la usa bien en su base de código, tendrá menos problemas para cambiar a una escuela de prueba diferente o reemplazar herramientas cuando surja la necesidad. Finalmente, recuerda que el mejor enfoque siempre depende del contexto de tu equipo. Los dobles de prueba también son muy importantes para el área instrumentada de la pirámide, pero deben usarse con un poco más de precaución. Cargar recursos de Android y ejecutar pruebas con un emulador o un dispositivo real es ciertamente un proceso costoso, quizás más costoso que la gran mayoría de las pruebas no instrumentadas que integran la mayor cantidad posible de componentes reales y, por lo tanto, usan menos duplicados de prueba. Un error común que veo es que las pruebas de IU reemplazan ViewModels con dobles. En mi opinión, Cuanto más costosa sea la prueba, más cerca debe estar de la realidad, lo que significa que debe probar más código. El uso de dobles para reemplazar ViewModels en las pruebas de interfaz de usuario simplemente da como resultado una prueba compleja y lenta con una cobertura de código deficiente. Otro punto importante a tener en cuenta es que la mayoría de las herramientas (como MockK o Mockito) manipulan el código de bytes para crear simulacros y stubs. Debido a que Android usa su propia máquina virtual y genera bytecode con un formato específico (Dalvik), algunas de estas herramientas tienen limitaciones para crear dobles en esta capa instrumentada. Por estas razones No desaconsejo el uso de dobles para las pruebas instrumentadas. Salvo dos situaciones:

  • Para reemplazar las dependencias en los límites de E/S, como backends y bases de datos, con dobles más rápidos y deterministas. Por ejemplo, use herramientas como MockWebServer.
  • O para reemplazar herramientas de terceros que son difíciles de configurar en pruebas instrumentadas, p. B. Clases de Firebase.

Doble estrategia de test en pruebas instrumentadas Las herramientas de inyección de dependencia como Dagger, Hilt o Koin y Product Flavours pueden ser grandes aliados a la hora de utilizar test double en la capa instrumentada. Como vimos en las secciones anteriores, tenemos dobles que tienen propósitos similares pero se implementan y configuran de manera diferente. Discusiones sobre cómo debe usar los dobles y qué compensaciones hay fácil configuración y mantenimiento han generado muchos debates y publicaciones interesantes en la comunidad de desarrolladores. La relación entre la fragilidad de las pruebas y la refactorización es uno de los temas más comunes que verá. Solo algunos ejemplos 👇 La publicación de Ryan Harter promocionando su (gran) artículo sobre la sustitución de simulacros por falsificaciones. Quizás el término espía (en lugar de falso) sería más apropiado en su artículo. La publicación de Zac Sweers parece insatisfecho con la forma en que Mockito genera los dobles de prueba, lo que, según él, contribuye a la fragilidad de la prueba. La publicación de Jake Wharton argumenta en contra del uso de simulacros, probablemente siguiendo un enfoque de prueba agradable. Ahora debería poder responder, o al menos comprender, las preguntas formuladas en la introducción del primer artículo:

  • «Tenemos que burlarnos de esta dependencia y todo funcionará bien» 🙌
  • «¡Evita usar simulacros!» 😱
  • «¿Mocks vs. Stub?» ⚔️
  • «Usa falsificaciones en lugar de simulacros» 🤔

Los dobles de prueba (comúnmente denominados simulacros) son muy importantes para su estrategia de prueba. Conozca bien su concepto, comprenda sus compensaciones y utilícelos para probar su aplicación de Android de una manera fácil y escalable. Si tiene comentarios o preguntas, envíelas a Gorjeo o en los comentarios de esta publicación. ¡Muchas gracias!

Deja una respuesta

Tu dirección de correo electrónico no será publicada.