sintesis

Códigos disponibles en git

¿Por qué?

  • Si las pruebas son viscosas, difíciles de leer o de escribir o …​

  • Si las pruebas son rígidas, difíciles de mantener o extender o …​

  • Si las pruebas son difíciles de ejecutar, lentas o acopladas a un entorno o …​

  • Entonces las pruebas no mejoran la documentación del SUT, para corregirlo e incrementarlo

  • Entonces las pruebas no mejoran la calidad, sin retroalimentación ante cambios del SUT

  • Entonces los riesgos son altos a falta de una buen Red de Seguridad

¿Qué?

  • Son pruebas automáticas, habitualmente funcionales, cuyo SUT es una clase implantadas habitualmente por el propio desarrollador del SUT

  • Pero con esta definición pocas clases cumplen el criterio

  • Una relajación sería, aquellas pruebas cuyo SUT no tienen DOC’s fuera de los paquetes del proyecto pero excluyendo cuando acceden fuera de la memoria de ejecución. Por ejemplo, acceso a persistencia, comunicaciones, …​

¿Para qué?

  • Si las pruebas son fluidas, fáciles de leer o de escribir o …​

  • Si las pruebas son flexibles, fáciles de mantener o extender o …​

  • Si las pruebas son fáciles de ejecutar, rápidas y no acopladas a un entorno o …​

  • Entonces las pruebas mejoran la documentación del SUT, para corregirlo e incrementarlo

  • Entonces las pruebas mejoran la calidad, con retroalimentación ante cambios del SUT

  • Entonces los riesgos son bajos por una buena Red de Seguridad

¿Cómo?

  • Fáciles de Ejecutar

    • Automatizadas

    • Auto-verificables

    • Repetibles

    • Independientes

    • Rápidas

  • Fáciles de Mantener

    • Profesionales

  • Fáciles de Leer/Escribir

    • Cohesivas

    • Sencillas

    • Expresivas

  • Mejoran la Comprensión del SUT

    • Parte de la Especificación

    • Parte de la Documentación

  • Mejoran la Calidad

    • Repelentes de Errores

    • Localizan Defectos

  • Reducen los Riesgos

    • Inocuas

    • Red de Seguridad

Fáciles de Ejecutar

  • Las pruebas automatizadas se ejecutarán con frecuencia sólo si son realmente fáciles de ejecutar

  • Un clic de un botón, o atajo de teclado, es todo lo necesario para obtener la valiosa retroalimentación que proporcionan la ejecución de pruebas: verde o rojo!

  • Automatizadas

  • Autoverificables

  • Repetibles

  • Independientes

  • Rapidas

Automatizadas

  • La prueba no requiere intervención humana para su ejecución más que un click para disparar el conjunto de pruebas deseado

Antipatrón Descripción Ejemplo
  • Prueba Interventora

  • Una prueba cuya ejecución requiere de la intervención humana para suministrar datos por teclado, configuraciones previas por la existencia de un fichero vacío, …​

Autoverificables

  • La prueba no requiere intervención humana para evaluar su resultado en verde, o sea, aplicar el Principio Hollywood, “No me llames, ya te llamaré”.

Antipatrón Descripción Ejemplo
  • Prueba Bocazas

  • Una prueba que llena la consola con mensajes de diagnóstico, de logs, depuración, …​ incluso cuando pasan los test.

Repetibles

  • Las ejecuciones consecutivas del mismo conjunto de pruebas desprenden el mismo resultado cuantas veces sea, no la primera diferente de la segunda o de forma “aleatoria”, cuantas veces sea.

Antipatrón Descripción Ejemplo
  • Prueba con Abundantes Sobras

  • Una prueba que crea datos que se guardan en algún lugar y otra prueba los reutiliza para sus propios fines siendo dependiente del orden de ejecución de la prueba.

Independientes

  • Las ejecuciones del mismo conjunto de pruebas desprenden el mismo resultado independientemente del entorno: local, integración o pre-producción

Antipatrón Descripción Ejemplo
  • Prueba con Dependencia oculta

  • Una prueba que requiere que existan datos en alguna parte antes de correr. Si los datos no se rellenaron, la prueba falla sin dejar apenas explicación.

Rápidas

  • El conjunto de pruebas de regresión se ejecuta en segundos como máximo

Antipatrón Descripción Ejemplo
  • Prueba Lenta

  • Una prueba que se ejecuta de forma increíblemente lenta. Se evita con dobles de algún DOC lento.

Fáciles de Mantener

  • Inevitablemente, queremos hacer muchos tipos de cambios en el código a medida que un proyecto se desarrolla y sus requisitos evolucionan.

  • Profesionales

  • Por esta razón, queremos escribir nuestras pruebas de tal manera que el número de pruebas afectadas por cualquier cambio sea bastante pequeño.

  • Eso significa que necesitamos minimizar la superposición entre las pruebas.

  • También debemos asegurarnos de que los cambios en el entorno de prueba no afectan nuestras pruebas;

  • Lo hacemos aislando el SUT del medio ambiente tanto como sea posible. Esto resulta en pruebas mucho más robustas.

  • Idealmente, sólo un tipo de cambio debe hacer que una prueba requiera mantenimiento.

  • Los cambios del sistema que afectan al código de prueba en la preparación del SUT o su desmontaje pueden encapsularse en los Métodos de Utilidad de prueba (@Before, @BeforeClass, @After y @AfterClass) para reducir aún más el número de pruebas directamente afectadas por el cambio.

  • Se debe diseñar el código de pruebas de tal forma que se controlen las posibles oleadas de cambios en las prueba por un pequeño cambio del SUT porque no debe frenar el desarrollo

Profesionales

  • El código de pruebas debe cumplir los principios de diseño (por ejemplo, SOLID, GRASP, KISS, DRY, YAGNI, Patrones de Diseño, …​) igual que el código de producción

Antipatrón Descripción Ejemplo
  • Comentario engañoso

  • La misma situación que en el código de Producción

  • Dependiente de la creación

  • Un cambio de constructor afecta a multitud de pruebas. Se evita con Patrón Builder, o también conocido como Object Mother en el ámbito de pruebas, "madre/generador" de objetos

Fáciles de Leer/Escribir

  • Probar el código de pruebas es un esfuerzo muy complicado y sería un problema recursivo: probar el código de pruebas que prueba el código de pruebas, …​

  • El código de pruebas tiene que ser fácil de leer/escribir porque solo tenemos nuestros ojos para permitir detectar errores en el código de pruebas …​ así que deben ser muy sencillas por la limitación humana, ya que programar es difícil cuando exige acumular mucha información en la cabeza.

  • Tenemos que concentrarnos en la prueba, no en codificar la prueba.

Cohesivas

  • Cada prueba se debe centrar en un único asunto, evitando métodos que comprueba varias características del SUT porque, en caso contrario, no permitirá la localización de errores. Por tanto, cada prueba debe cumplir el Principio de Única Responsabilidad

Antipatrón Descripción Ejemplo
  • Prueba Extraña

  • Una prueba que ni siquiera pertenece a la clase porque es parte de otra prueba de otro SUT, muy probablemente usado por el SUT de la prueba pero saltándose la interacción que hay entre ambos

  • Dependiente de la creación

  • Un cambio de constructor afecta a multitud de pruebas. Se evita con Patrón Builder.

Sencillas

  • Evitar que la prueba verifica demasiada funcionalidad en una sola prueba.

    • Hay que partir en varios métodos atómicos de prueba, incluso en varias clases de prueba.

    • Un método de prueba raramente excede las 10 líneas de código.

Antipatrón Descripción Ejemplo
  • Prueba con Aserciones Sobreprotegidas

  • Una prueba que incorpora aserciones innecesarias. Por ejemplo, porque aplica la programación defensiva en su código protegiendo el fallo con una fea NullPointerExceptiony, en su lugar, dejando que la prueba falle con gracia con un error de aserción bien formulado

  • Prueba Cotilla

  • Una prueba que compara la salida “completa” del ejercicio del SUT que se está probando cuando en realidad solo se precisa parcialmente para la verificación de la característica, aumentando los esfuerzos de comprensión y mantenimiento

  • Prueba de Múltiple Personalidad

  • Una prueba verifica varias características del SUT en un mismo método de prueba. Lo correcto será partir en varios métodos, incluso en varias clases.

  • Prueba con Lógica Condicional

  • Una prueba contempla sentencias alternativas que dificultan la comprensión de la prueba

Expresivas

  • Maximizar el objetivo de la prueba

  • Una prueba no debe contemplar una brecha de expresividad demasiado grande entre el código de la prueba y los conceptos del dominio que está tratando de expresar la prueba.

  • Esta "brecha de expresividad" se puede resolver creando una biblioteca de Métodos de Utilidad de Prueba que constituyen un lenguaje de prueba específico del dominio.

  • Esta colección de métodos permite a las pruebas expresar los conceptos que desean probar sin tener que traducir sus pensamientos en código mucho más detallado.

  • Métodos de creación y aserción personalizadas son buenos ejemplos de los bloques de construcción que conforman dicho lenguaje de nivel superior.

Antipatrón Descripción Ejemplo
  • Prueba obscura

  • Una prueba con nombre poco descriptivo (por ejemplo, test1, …​) cuando debería expresar con total claridad la intención del mismo. Se evita con nombres descriptivos, sea la longitud que sea necesaria para el identificador.

  • Prueba con Números Mágicos

  • Una prueba con literales en el código que no revelan su significado. Se evita con una función que dé nombre explícitamente a cada literal.

  • Prueba con Detalles Incidentales

  • Una prueba con sobreinformación de cómo se crea, ejercita o verifica un SUT: algoritmos, valores necesarios para la construcción de la prueba,…​ Se evita con un único nivel de abstracción a través de buenos nombres de métodos privados de la prueba que encapsulen la sobreinformación

  • Prueba con Sermón de Preparación

  • Una prueba tiene una preparación muy larga y no se ven los datos relevantes de tal forma que no se sabe lo que se está probando. Hay que usar métodos privados para encapsular detalles indeseados

  • Prueba con Aserciones Primitivas

  • Una prueba que no parece tener sentido porque la justificación e intención de la prueba está oculta tras palabras y números aparentemente sin sentido

  • Prueba con Híper-aserciones

  • Una prueba debería tener una sola razón para fallar

Mejoran la Comprensión del SUT

  • Deben mostrar al lector cómo supuestamente trabaja el código

  • Las pruebas de caja negra describen los requisitos funcionales del componente software

Pruebas como especificación

  • Las pruebas nos permiten especificar el comportamiento a través de distintos escenarios con suficiente detalle nos ayudan a identificar áreas donde los requisitos son ambiguos o contradictorios, como una “especificación ejecutable”.

  • Tal análisis mejora la calidad de la especificación, lo cual mejora la calidad del software así especificado.

Mejoran la Documentación

  • Sin pruebas automáticas, necesitaríamos volcarnos sobre el SUT intentando responder la pregunta, ¿cuál sería el resultado si …​?

  • Con pruebas automáticas, se permite la experimentación inmediata, retroalimentación

Mejoran la Calidad

  • verificar, ¿es está construyendo el sistema correctamente? y

  • validar, ¿se está construyendo el sistema correcto?

Repelentes de Errores

  • Las pruebas previenen de errores antes de entregar el código desde que son introducidos gracias a la reusabilidad de las pruebas de regresión

Localizador de Defectos

  • Las pruebas de aceptación por parte del cliente nos dicen que algún comportamiento esperado por el cliente no está funcionando.

  • Las pruebas unitarias nos dicen por qué

    • Pero si son bastante pequeñas, es decir, probamos solamente un solo comportamiento en cada una.

    • Así, deberíamos ser capaces de localizar el error rápidamente basado en qué prueba falla, por triangulación.

Reducen los Riesgos

  • La planificación de pruebas debe revisar todos los riesgos del proyecto y discutir sobre la clase de riesgos que podrían ser mitigados, al menos parcialmente, a través de las pruebas automáticas

  • La Red de Seguridad debe ser un juego de pruebas exhaustivo y muy superior frente a las pruebas manuales de validación por parte del cliente.

  • Por tanto, debe alcanzarse una alta cobertura

  • Inocuas

  • Red de Seguridad

Inocuas

  • Las pruebas no deben introducir nuevos fallos por lo que no se debe modificar el SUT con “nuevos problemas”.

  • Por tanto, se debe mantener toda la lógica de la prueba fuera del SUT, de tal forma que cualquier código específico de la prueba debe ser aportado por la prueba y sólo en el entorno de prueba.

Antipatrón Descripción Ejemplo
  • Prueba Manazas

  • Una prueba que incorpora/modifica código de producción para facilitar su codificación: añadir métodos get, cambiar la signatura del método para devolver un valor esperado, …​

Red de Seguridad

  • Las pruebas deben evidenciar todos los fallos de tal forma que los errores no detectados en producción sean muy infrecuentes.

Antipatrón Descripción Ejemplo
  • Prueba Infalible

  • Una prueba se escribió para pasar en lugar de para fallar primero. Como desafortunado efecto colateral, sucede que el test siempre funciona, aunque debiese fallar, creando una falsa sensación de seguridad

  • Prueba Errática

  • Una prueba que a veces pasa y a veces falla.

    • El poder de la barra roja disminuye significativamente cuando se ve regularmente, se comienza a ignorar y se ha perdido gran parte del valor de las pruebas sin la retroalimentación que indica si se ha introducido un error.

  • Prueba Incompleta

  • Una prueba que parece confiable porque ha sido probado a fondo cuando, de hecho, no lo hace

  • Prueba con Bajas Expectativas

  • Una prueba que toma un atajo con el que puede ayudarle a ir más rápido pero no prueba la característica en toda su extensión.

Sintesis

sintesis

Bibliografía

Obra, Autor y Edición Portada Obra, Autor y Edición Portada
  • xUnit Test Patterns: Refactoring Test Code

    • Gerard Meszaros

    • Addison-Wesley Educational Publishers Inc; Edición: 01 (21 de mayo de 2007)

xUnitTestPatternsRefactoringTestCode

  • Effective Unit Testing

    • Lasse Koskela

    • Manning Publications Company; Edición: 1 (16 de febrero de 2013)

EffectiveUnitTesting

  • The Art of Software Testing

    • Glenford J. Myers

    • John Wiley & Sons Inc; Edición: 3. Auflage (16 de diciembre de 2011)

TheArtofSoftwareTesting

  • Pragmatic Unit Testing in Java with JUnit

    • Andy Hunt, Dave Thomas

    • The Pragmatic Programmers (1674)

PragmaticUnitTestinginJavawithJUnit

  • Testing Computer Software

    • Cem Kaner,Jack Falk,Hung Q. Nguyen

    • Wiley John + Sons; Edición: 2nd (12 de abril de 1999)

TestingComputerSoftware

  • Diseno Agil con TDD

    • Ble Jurado, Carlos

    • Lulu.com (18 de enero de 2010)

DisenoAgilconTDD

  • Test Driven Development

    • Kent Beck

    • Addison Wesley; Edición: 01 (8 de noviembre de 2002)

TestDrivenDevelopment

  • Growing Object-Oriented Software, Guided by Tests

    • Steve Freeman

    • Addison-Wesley Professional (12 de octubre de 2009)

GrowingObjectOrientedSoftware,GuidedbyTests

  • How Google Tests Software

    • James A. Whittaker,Jason Arbon,Jeff Carollo

    • Addison-Wesley Educational Publishers Inc; Edición: 01 (2 de abril de 2012)

HowGoogleTestsSoftware

  • Pragmatic Project Automation: How to Build, Deploy, and Monitor Java Apps

    • Mike Clark

    • The Pragmatic Programmers (1900)

PragmaticProjectAutomation

Ponente

  • Luis Fernández Muñoz

setillo

  • Doctor en Inteligencia Artificial por la UPM

  • Ingeniero en Informática por la UMA

  • Diplomado en Informática por la UPM

  • Profesor Titular de ETSISI de la UPM