Automatización de pruebas: the wrong way

Si, lo sé, el título es un poco marketinero y bastante incorrecto como suele pasar con afirmaciones tan extremas.

La cuestión es que hoy en día muchas organizaciones/equipos con la intención de abrazar los beneficios de Agile/DevOps descubren que es necesario tener pruebas automatizadas. Hasta ahí vamos bien.

La cuestión se empieza a torcer cuando para automatizar la pruebas ponen gente exclusivamente para hacer eso mientras que los desarrolladores siguen trabajando sin alteración alguna en su dinámica. ¡ooops! No es por ahí.

Es cierto que es necesario tener pruebas automatizadas y que en general sin importar cómo se hagan, es mejor que nada. Pero la teoría y la evidencia indican que en un contexto Agile/DevOps la automatización de pruebas requiere del involucramiento activo de los desarrolladores. Los desarrolladores deben hacer su parte automatizando las pruebas unitarias y luego trabajando de forma cercana con usuarios y testers en las pruebas de aceptación. Podrá después haber algún trabajo adicional de automatización de pruebas para generar un set de regresión, pero si lo desarrolladores hacen lo que acabo de mencionar, entonces este esfuerzo adicional agrega poco valor.

Claro que esto no es trivial, nunca dije que lo fuera. Se requiere de algunas habilidades (duras y blandas) adicionales respecto del enfoque tradicional de desarrollo y testing. Para poder escribir pruebas unitarias necesitamos saber hacerlo pero más allá de eso necesitamos que la arquitectura/código de nuestra aplicación nos lo permitan. También se requieren de algunas habilidades de comunicación/colaboración para que desarrolladores y testers trabajen más cerca y de forma temprana.

Algunas de estas cuestiones las menciono en esta entrevista del año pasado y también la trato con cierta profundidad en mi Taller de Prácticas Técnicas.

Sobre las poco conocidas Pruebas end-to-EDGE

Tengo la sensación que este tipo de pruebas no es muy conocido y al mismo tiempo es un tipo de prueba que me parece fundamental para poder trabajar en un esquema de entrega continua. Paso a explicar.

En la actualidad es muy común que una aplicación tenga dependencias que no están bajo nuestro control, típicamente alguna API de un servicio externo. Ejemplos típicos podrían incluir un servicio de pagos o un proveedor de información climática. Esos servicios externos pueden ser parte de nuestra misma organización o no, pero en ambos casos el punto en común es que no tenemos control sobre ellos.

Muchas veces esta dependencia resulta un impedimento para poder testear nuestra aplicación y aquí está el punto clave a la hora de querer implementar una estrategia de entrega continua. Si queremos trabajar en un esquema de entrega continua tenemos que poder testear (casi) todo nuestro código sin depender de la disponibilidad del servicio externo.

En una prueba end-to-end testeamos todo nuestro código incluida la interacción con las dependencias externas.

En una prueba end-to-EDGE testeamos todo nuestro código sin «tocar» las dependencias externas. Esto lo hacemos utilizando «un doble de prueba» de la dependencia. Una forma de implementar esto es usando una herramienta tipo Wiremock.

Cabe destacar aquí, que en estos tests end-to-edge, nuestra aplicación «no sabe» que está interactuando con un doble de prueba, pues lo único que hace es tirar un request sin saber quién está del otro lado. Al mismo tiempo como ese doble de prueba está bajo nuestro control, tenemos la posibilidad de generar respuestas que nos permitan ejercitar distintos escenarios, algo que muchas veces no es posible con las pruebas end-to-end, pues en la prueba end-to-end no tenemos control sobre esa api/depedencia externa. ¿Cómo haríamos para probar el comportamiento de nuestra aplicación cuando esa api/dependencia externa falla? ¿Llamamos al dueño de la api/dependencia externa y le decimos que la apague un rato para poder probar el escenario de error? No parece factible, lo que debemos hacer no es una prueba end-to-end, sino una prueba end-to-edge que utilice nuestro doble de prueba en lugar de la api/dependencia externa real.

Una última consideración, si vamos a hacer pruebas end-to-edge utilizando un doble de prueba de la api/dependencia externa, debemos entonces también tener alguna prueba de integración que verifique la correcta interacción con la api/dependencia externa, pues sin eso corremos el riesgo de que la api/dependencia externa cambie y nosotros no nos enteremos pues confiamos en la corrección de nuestro doble de prueba que podría quedar desactualizado. Esas pruebas de integración con la api/dependencia externa no es necesario que sean pruebas end-to-end, sino que basta con que sean pruebas puntuales del componente/conector que encapsula la interacción con esa api/dependencia externa. Finalmente es importante que esas pruebas de integración se ejecuten periódicamente aún cuando no haya cambios en nuestro código, pues puede que se produzcan cambios en el api/dependencia externa y no nos avisen.

Para intentar clarificar varias de estas ideas he grabado una par de videos explicando esto con gráficos y código.

Testing sin Testers, una mirada distinta al control de calidad

Tradicionalmente la actividad de control de calidad, usualmente denominada testing, ha sido llevada a cabo por personal con el rol específico de tester. Asimismo es una actividad que en muchos contextos se realiza de forma manual e incluso en algunas organizaciones de una forma no sistematizada y completamente ad-hoc.

Si bien las iniciativas Agile/DevOps han propuesto algunos cambios significativos en la forma de realizar el testing, muchas organizaciones siguen sufriendo estragos con el control de calidad en parte por seguir estrategias contrarias en esencia a lo propuesto por estas iniciativas.

Es por esto que mañana, jueves 16 a las 11:30 hs., estaré disertando sobre este tema en el contexto de las Jornadas de Calidad de Software y Agilidad organizadas por la Universidad Nacional de Misiones y que se realizarán en formato mixto presencial-online. Los interesados pueden registrarse aquí.

En mi disertación repasaré los puntos centrales del movimiento Agile/DevOps en lo referente al testing, compartiré varias experiencias de implementación que me han resultado efectivas y daré algunos consejos para quienes quieran aventurarse al cambio.

Sobre las Pruebas de Integración

En mi opinión no hay una definición única sobre qué es una prueba de integración. En términos muy generales suele asumirse que prueba de integración verifica la integración de dos (o más) componentes. La cuestión (o diferencia) radica en qué son esos componentes.

Algunos (¿la mayoría?) considera que esos dos componentes son componentes de nuestra propia autoría, por ejemplo: tengo dos clases desarrolladas por mi con algún tipo de dependencia entre ellas, claseA y claseB, entonces mi prueba de integración busca verificar la correcta interacción de esas dos clases entre sí.

Otros, donde me cuento yo, pensamos más en línea con la propuesta de Freeman. En este caso uno de los componentes en la prueba de integración no está bajo nuestro control, o sea: no lo desarrollamos nosotros. Esto hace que el foco de la prueba de integración pase por verificar la interacción del código que está bajo nuestro control con código que no lo está. Un caso típico es cuando nuestra aplicación debe interactuar con una aplicación/servicio/api externo como ser una API REST desarrollada por otro equipo/organización. En este caso, no tiene ningun sentido utiliza mocks (dobles de prueba) porque justamente lo que quiero verificar es la interacción con ese sistema externo, entonces de nada sirve reemplazar ese sistema externo con un doble de prueba.

Testear en Producción, pero seriamente

Para muchos «testear en producción» es una acción temeraria, un pecado, una situación riesgosa, indeseada. Y es típicamente así cuando el único testing que hacemos es en producción, cuando cada cambio que introducimos en producción es muy grande, cuando los procesos de despliegue son manuales y esporádicos.

Pero si trabajamos en pequeños incrementos, hacemos TDD, testeamos de forma sistematizada, automatizamos la tareas repetitivas y desplegamos de forma frecuente, entonces no hay mucho de que temer. Esto es lo que comentaba Nayan Hajratwala en su sesión «Embrace these Three Fearsome Words: Test in Production» en Agile 2023. Estoy muy de acuerdo con esto pues esta propuesta esta muy en línea con lo que yo mismo hice las pocas veces que decidí probar en producción.

Un habilitador imprescindible en mi opinión para poder testear en producción es tener la capacidad de «segmentar funcionalidades por grupos de usuarios», esto es: poder definir subconjuntos de usuarios para que solo ellos puedan acceder a ciertas funcionalidades. Algunos autores llaman a esto «Dark launch«. Esta práctica no es más que una variación de la práctica de feature toggles que permite desacoplar el deploy («instalación» en producción) y el release (disponibilización para el usuario).

Alguien podrá preguntarse, ¿porque testear en producción si tengo la posibilidad de testear en ambientes previos? Puede haber varias razones, comparto dos que son las que he experimentado:

  1. Hay ocasiones en las que testeamos en producción porque hay ciertas condiciones que solo están disponibles en dicho ambiente, típicamente integraciones con otros sistemas. En ese caso lo que estamos haciendo en producción es una prueba de integración y la hacemos luego de haber testeado todo nuestro código de forma previa al despliegue en producción.
  2. Hay otras ocasiones que testeamos en producción para tener feedback real a nivel «negocio». Caso típico: queremos validar una hipótesis de negocio, queremos ver como nuestros clientes/usuarios reaccionan ante una nueva funcionalidad o variante de una funcionalidad existente. En este caso no estamos testeando si el software hace lo que habíamos pensado, sino que estamos testeando una idea de negocio.

Recursos de Testing 3.0

Ayer estuve participando en el ciclo de Tech Talks facilitado por Arcadio Abad. Estuve hablando sobre un conjunto de prácticas que he dado en llamar Testing 3.0.

La charla fue efectivamente una charla de ida y vuelta con Arcadio quien a su vez transmitía las preguntas de la audiencia. Me sentí muy cómodo con el formato y creo que pude dar respuesta a todas las preguntas que Arcadio me transmitió(aunque creo que por el chat pasaron algunas otras que pueden haber quedado sin respuesta por cuestiones de tiempo).

Durante la charla hablé de varias cuestiones pero la gran mayoría relacionadas a la idea de usar los tests como especificación de requerimientos y guía del desarrollo (lo que usualmente se conoce como BDD). Comparto aquí algunos recursos al respecto:

  • Los libros de BDD de Rose y Nagy son simplemente excelentes.
  • El libro «Specification by Example» de Goyco también es excelente, es bastante más extenso porque cubre muchas cuestiones.
  • Unos videos que muestran la técnica de BDD/TDD en acción: parte 1 y parte 2
  • Varios artículos de mi blog sobre esta técnica

Y si quieren aprender más de BDD y no tienen un perfil muy técnico, los invito a que se sumen a mi Taller de Prácticas Técnicas para Scrum Masters (al margen del nombre, vemos cuestiones que son relevantes incluso para gente que no es Scrum Master)

Tech Talk: Testing 3.0+

Este jueves 9 de marzo estaré participando del ciclo de charlas técnicas organizado por la gente de Abstracta. En dicho espacio hablaré sobre mi visión actual del testing en general y del rol de tester en particular. La charla está pensada como una continuidad/profundización de la charla que dí en la QualitySense Conf en diciembre 2022 (video disponible aquí) y resume lo que actualmente enseño en la universidad.

Algunos puntos centrales de la charla:

  • Parte del testing lo deben hacer los desarrolladores.
  • Los testers deben trabajar muy cerca de la gente de negocio.
  • Los casos de pruebas deben generarse temprano en la iteración, incluso algunos casos de prueba deberían estar claros al finalizar la reunión de planificación de la iteración.
  • La gran mayoría del testing debe estar automatizado.
  • La ejecución de la mayoría de los test debe poder ejecutarse sin necesidad de utilizar un ambiente particular.
  • Testear algunas cuestiones en producción es válido, pero ello requiere planificación de cara a poder contemplarlo a nivel de desarrollo e infraestructura.

Varios de los puntos anteriores impactan directamente en las tareas y habilidades del tester y sé también que algunos pueden resultar polémicos. Esto es una buena excusa para sumarse la charla, es gratuita pero require registración.

WOPR, QSConf y Testing 3.0

Del 5 al 10 de diciembre voy a estar en Montevideo para participar de estos dos eventos.

El Workshop on Performance and Reliability (WOPR) es una evento cerrado que se accede por postulación/invitación y que tiene ~25 cupos por edición. Hay un grupo de organizadores que asegura la continuidad del evento (esta será la edición 29), luego hay una empresa del rubro (performance & reliability) que «hostea» el evento y se encarga de las cuestiones logísticas. Los interesados en participar envían una propuesta, la misma es evaluada por los organizadores y en base a eso se envían las invitaciones. En esta edición la empresa host es Abstracta, pueden encontrar más información al respecto del WORP en este post de Federico Toledo.

A continuación del WORP y organizado también por Abstracta, tendrá lugar el viernes 9 de diciembre en la torre Antel, la Quality Sense Conf. Esta conferencia será gratuita y en formato híbrido. En este contexto voy a estar dando una charla que he titulado «Testing 3.0» :

Las estrategias de testing y el rol de tester ha ido cambiando a lo largo del tiempo influenciado, entre otras cuestiones, por los avances tecnológicos y metodológicos. Desde los procesos «cascadosos» y las aplicaciones mainframes, pasando por los métodos ágiles, las aplicaciones de escritorio, a las aplicaciones web, mobile y el auge de DevOps. Los desafíos que enfrenta la industria del software en la actualidad han generado importantes desafíos y oportunidades para quienes trabajan en cuestiones de testing y calidad en general. En esta charla veremos algunas de esas oportunidades y cómo prepararnos para sacarle el mayor provecho. 

Testing automatizado en React-Native

Luego de 6 meses trabajando a diario con React-Native he logrado hacerme una idea bastante clara sobre esta temática que me parece no está suficientemente bien documentada.

En primer lugar tengamos presente que con React-Native vamos a generar aplicaciones móviles para Android y/o iPhone con lo cual un tipo de prueba automatizada que podremos realizar es directamente sobre el binario nativo que se instala en el dispositivo, serían las denominadas pruebas end-to-end que muchas veces se hacen en forma manual. Su ejecución requiere por un lado del uso de un emulador (o incluso se puede usar un dispositivo físico) y por otro, el uso de algún driver/conector que nos permita interactuar con la aplicación desde la perspectiva del usuario. Como estas pruebas van directamente contra el binario/ejecutable, tenemos independencia para codearlas en un lenguaje distinto al que utilizamos para codear la aplicación. La pieza central en este tipo de pruebas es el driver/componente que nos va a permitir manipular la aplicación emulando la interacción del usuario. En este sentido una de las herramientas más populares es Appium y otra es Detox.

Sacando las pruebas end2end, tenemos distintos tipos de pruebas de índole más técnico, de componentes/unitarios. Estas pruebas sí las estaremos codeando con la misma tecnología que codeamos la aplicación. En el caso de estar trabajando con React Native estas pruebas las vamos a codear con JavaScript. Aquí también entran en juego distintas herramientas. La primera de ellas es el framework de testing que utilizaremos para escribir los casos de prueba y agruparlos en suites. Aquí tenemos varias alternativas (como suele ocurrir habitualmente en JavaScript) pero al trabajar con React se suele utilizar Jest que es la herramienta recomendada en la documentación oficial de React. Jest nos va a permitir escribir casos de prueba sobre objetos/funciones JavaScript, sean estos componentes React o simples objetos planos «vanilla JavaScript». Una «bondad» que tiene Jest es que trae nativamente funcionalidades de mocking/stubbing con lo cual nos ahorramos de tener que incluir en nuestro proyecto otro framework/herramienta para mocking.

Si en nuestras pruebas queremos testear componentes React-Native, en particular el rendering, en primera instancia podemos utilizar el Test Renderer que es parte del core de React. También como parte del core de React tenemos las Test Utils que ofrecen un conjunto muy útil de funciones utilitarias.

También tenemos la posibilidad de utilizar React-Native Testing Library que debemos instalar por separado (yarn add –dev @testing-library/react-native). Esta librería construída sobre la base del Test Renderer y agrega un conjunto de funciones utilitarias de gran utilidad.

Situaciones de Diseño #1

Hace tiempo venia con la idea de publicar una serie de videos sobre diversas técnicas/patrones/recomendaciones para lidiar con situaciones de diseño habituales. Finalmente ayer decidí poner manos a la obra y publiqué el primer video de esta serie.

La situación que aborda este primer video es cómo lidiar con lógica que depende de fechas de una forma testeable. Muchas veces cuando tenemos lógica que depende de la fecha actual, estamos tentados de utilizar directamente una llamada al componente nativo de fecha. El problema con este camino es que no resulta testeable. La clave está en encapsular el acceso a la fecha del sistema de manera tal de poner utilizar un doble de prueba (mock) en el momento de hacer nuestra pruebas automatizadas.

Les dejo aquí el video y los invito a que me dejen en los comentarios alguna otra situación que les gustaría que aborde en próximos videos.