Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

TDD Course (Spanish)

1 977 vues

Publié le

Exercises: http://code.theprogrammingchronicles.com/tdd-exercises/#overview

Publié dans : Logiciels
  • Soyez le premier à commenter

TDD Course (Spanish)

  1. 1. TEST DRIVEN DEVELOPMENT IN ACTION ACCEPTANCE TEST DRIVEN DEVELOPMENT CONTINUOUS INTEGRATION Pedro Ballesteros Herranz <nitroduna@gmail.com> @pmbah
  2. 2. OBJETIVOS DEL CURSO “To turn you into test-infected developers”1 “To spread the TDD epidemic” “Test-Driven Development is addictive” 1 Referencias  “tests-infected”: Termino ampliamente extendido para nombrar desarrolladores que hacen uso de tdd o unit testing (búscalo en google).  http://junit.sourceforge.net/doc/testinfected/testing.htm  JUnit in Action (Manning Publications) http://www.manning.com/tahchiev/ “The statistics show that people get easily “infected” with the unit-testing philosophy. Once you get accustomed to writing tests and see how good the feeling of someone protecting you from possible mistakes is, you will wonder how was possible to live without unit-testing before.”  “Developers who adopt these techniques self-identify as "test-infected" meaning they are quite literally addicted to writing unit tests for their code which is something counter-intuitive to developers who have never experienced it.”
  3. 3. OBJETIVOS DEL CURSO  Aprender la metodología TDD experimentándola de forma practica  Entender que implican los distintos tipos de tests  Diferencias, cuando usar cada tipo, desarrolladores y testers.  ¿Qué tests se pueden (o conviene) automatizar? ¿Qué esfuerzo suponen?  ¿Qué tests se deben o no se deben utilizar desarrollando con TDD?  ¿Sólo consiste en “hacer primero los tests”? NO…ES MUCHO MAS  Test-First Development vs Test-Driven Development  TDD tiene sus limites: como superarlos y cuanto cuesta.  Ventajas, beneficios, limites, inconvenientes, vulnerabilidades, mitos, malentendidos, etc.  Practicas, Ejercicios, Ejemplos, Practicas, Ejercicios, Ejemplos, …
  4. 4. TEMARIO I  SOBRE LOS EJERCICIOS Y EJEMPLOS  INTRODUCCIÓN  INTRODUCCIÓN A EXTREME PROGRAMMING  CARACTERIZANDO TDD DESDE XP  INTRODUCCIÓN TDD Y ATDD  TEST UNITARIOS AUTOMATIZADOS  INTRODUCCIÓN CICLO DE DESARROLLO TDD  TEST DRIVEN DEVELOPMENT EXPLAINED  TIPOS DE TESTS  CICLO DE DESARROLLO DE TDD  PROPIEDADES FIRST DE LOS TESTS  PROPIEDADES SOLID PARA LA REFACTORIZACIÓN
  5. 5. TEMARIO II  UNIT TESTING FRAMEWORKS  DUMMY / FAKE / STUB / MOCK / SPY  VALIDACIÓN POR ESTADO Y POR INTERACCIÓN  TDD APLICADO A ENTORNOS ESPECÍFICOS  ANTI-PATRONES Y MALAS PRÁCTICAS  ACCEPTANCE TEST DRIVEN DEVELOPMENT EXPLAINED  INTEGRACIÓN CONTINUA  TDD – OBJETIVOS, BENEFICIOS Y CARACTERÍSTICAS  TDD – VULNERABILIDADES Y PELIGROS  TDD – MITOS Y MALENTENDIDOS  COMO EMPEZAR CON TDD
  6. 6. SOBRE LOS EJERCICIOS Y EJEMPLOS La impartición del curso está dirigida por ejemplos y prácticas de ejercicios TDD, que representan el desarrollo de una aplicación empresarial completa, pasando por las capas de presentación, negocio y persistencia (aunque sigue tratándose de un ejemplo simplificado). Algunas partes de la teoría en lugar de estar reflejadas en esta presentación están exclusivamente incluidas en los ejercicios.  Los ejercicios se han desarrollado enteramente aplicando la metodología TDD.  Los ejercicios están divididos a en secciones que representan avances de un solo paso del ciclo TDD (cada sección es una copia del paso anterior con un incremento adicional).  Algunas veces el avance de la solución representa la aplicación iterativa de varias iteraciones del ciclo TDD.
  7. 7. SOBRE LOS EJERCICIOS Y EJEMPLOS MATERIAL DISPONIBLE  PRESENTACIÓN ACTUAL Documentación y referencia de la teoría del curso. Muchas diapositivas tienen notas asociadas a modo de apuntes de la teoría.  ENUNCIADO DE LA APLICACIÓN a desarrollar Enunciado detallado de la aplicación sobre la que girarán los ejercicios, con apuntes de la teoría a modo de recordatorio.  EJERCICIOS Y EJEMPLOS Disponibles en el repositorio de github: https://github.com/theprogrammingchronicles/tpc-tdd-exercises  DOCUMENTACIÓN DE EJERCICIOS Y EJEMPLOS Todos los ejercicios cuentan con documentación detallada de la problemática a resolver, teoría a revisar y pasos a realizar. El código fuente cuenta con JavaDocs descriptivos de la problemática propuesta en cada ejercicio y su resolución. Documentación de ejercicios en: http://code.theprogrammingchronicles.com/tpc-tdd-exercises/
  8. 8. SOBRE LOS EJERCICIOS Y EJEMPLOS TECNOLOGÍAS Y LENGUAJES Lamentablemente no es posible la enseñanza práctica de TDD sin el apoyo sobre un lenguaje de programación o tecnología concreta. La parte práctica de este curso se ha desarrollado con Java, pero se ha evitado la dependencia de frameworks muy específicos, la extrapolación de la práctica a otros lenguajes de programación debería ser sencilla(*). MUY IMPORTANTE  Este curso no pretende ser un compendio de recomendaciones sobre el uso de herramientas Javas aplicadas a TDD.  La teoría y práctica es completamente independiente de:  Si se selecciona ant o maven como herramienta de build.  Si se selecciona Netbean, Eclipse, IntelliJ IDEA o UltraEdit como IDE.  Si se selecciona Hudson o Luntbuild como herramienta de CI.  Etc. * La adaptación de este curso a otros lenguajes tan solo requeriría la migración de los ejercicios y ejemplos.
  9. 9. TEST DRIVEN DEVELOPMENT INTRODUCCIÓN
  10. 10. INTRODUCCIÓN TDD nació como una de las prácticas de eXtreme Programming:  Actualmente ya es una metodología de desarrollo independiente.  Se está impartiendo como una metodología aislada de XP.  Ya se encuentra incluida como parte de otras metodologías más genéricas:  Rational Unified Process (RUP)  Agile Unified Process (AUP)  Open Unified Procress (Open UP)  SCRUM
  11. 11. INTRODUCCIÓN “Test-Driven Development es una Metodología de Desarrollo” “NO es una metodología de testing” “En primer lugar es una metodología de diseño y desarrollo, que como efecto lateral acaba proporcionando un código completamente(*) probado por Unit Tests automatizados” “La metodología proporciona un desarrollo del software a través de la aplicación iterativa, continua e incremental de tres pasos, de los cuales el primero es siempre la codificación de un small scaled test” (*) ¿Quiere eso decir que el sistema está probado al 100%?: NO, TDD no es un sustito de las técnicas de testing. TDD no elimina la necesidad de otros tipos de tests. No elimina el trabajo de un grupo de testers o QA. TDD si proporciona una barrera importante contra defectos. El número de bugs detectados en tests de más alto nivel disminuye, el trabajo del grupo de QA disminuye.
  12. 12. EXTREME PROGRAMMING INTRODUCCIÓN A EXTREME PROGRAMMING
  13. 13. INTRODUCCIÓN A EXTREME PROGRAMMING REFERENCIAS  http://www.extremeprogramming.org/  Guía rápida de eXtreme Programming con diagramas de flujo navegables.  http://www.xprogramming.com/  Información práctica de eXtreme Programming y TDD. Software, herramientas, librerías para multitud de lenguajes (xUnit, mocks, integración,…), entornos de desarrollo, etc.  Extreme Programming Explained: Embrace Change (2nd Ed), 2004, Kent Beck, Addison-Wesley Professional.  Libro escrito por el creador de eXtreme Programming.
  14. 14. INTRODUCCIÓN A EXTREME PROGRAMING HISTORIA La metodología fue creada y utilizada inicialmente por Kent Beck en 1996 (con ayuda de Ward Cunninghan y Ron Jeffires) en el proyecto de nóminas C3 (Chrysler Comprehensive Compensation) de Daimler- Chrysler. Tras terminar el proyecto, Beck escribió el libro Extreme Programming Explained, con el cual ganó el premio “Software Development Jolt Product Excelence Award”. Desde entonces se ha ido refinando la metodología, y han ido apareciendo variantes (Ej. Remote eXtreme Programming).
  15. 15. INTRODUCCIÓN A EXTREME PROGRAMMING MOTIVACIONES  1968, Crisis del Software (conferencia de la OTAN sobre software).  Los proyectos no terminaban en plazo.  Los proyectos no se ajustaban al presupuesto inicial.  Baja calidad del software generado.  Software que no cumplía las especificaciones.  Código inmantenible que dificultaba la gestión y evolución del proyecto.  Nace la “Ingeniería del Software”.  Construir software aplicando procesos de ingeniería.  Metodologías:  Trabajos previos de construcción “sobre papel”: Análisis, Especificación, Arquitectura, Diseño.  Esfuerzos invertidos en definir y fijar todo por adelantado y esfuerzos adicionales por controlar y contener el cambio.
  16. 16. INTRODUCCIÓN A EXTREME PROGRAMMING MOTIVACIONES  El panorama del software ha cambiado mucho desde 1968.  Ni se programa con tarjetas perforadas ni los ciclos de vida son tan largos.  Los procesos de ingeniería copiados de otras disciplinas no funcionan bien en el software.  No es sencillo tratar el software como la abstracción del mundo real.  El software es muy diferente a otras disciplinas.  Seguimos observando los mismos defectos.  Los proyectos no terminan en plazo y no se ajustan al presupuesto inicial.  El software no cumple especificaciones, es de baja calidad.  Seguimos encontrándonos con software inmantenible.  Los equipos de desarrollo acaban trabajando sin metodología. ¿Qué desarrollador cuenta con diagramas detallados de clase, de interacción o de estado antes de ponerse a codificar cada módulo?
  17. 17. INTRODUCCIÓN A EXTREME PROGRAMMING LO QUE NO ES Extreme Programming NO ES “Cowboy Coding”. • Ausencia de metodología. • Desarrolladores completamente autónomos y sin gestión externa. • Hago lo que quiero y como quiero. Extreme Programming NO ES “Death March Development” • Trabajo a marchas forzadas. • Estimaciones ajustadas y no realistas. • Falta de documentación y conocimiento global del proyecto. • Horas extras, fines de semana, vamos a meter más gente, ... “Con Extreme Programming se trabaja a un ritmo sostenible” (sustainable pace, las famosas 8h)
  18. 18. INTRODUCCIÓN A EXTREME PROGRAMMING LO QUE SI ES “Es una metodología ágil de desarrollo de software que pone más énfasis en la adaptabilidad que en la previsibilidad” Extreme Programming abraza el cambio, en lugar de intentar contenerlo. ¿Y POR QUÉ EXTREME? “Los elementos beneficiosos de las metodologías tradicionales de ingeniería del software se llevan a niveles extremos”
  19. 19. INTRODUCCIÓN A EXTREME PROGRAMMING OBJETIVOS DE LA METODOLOGÍA  Adaptarse a los cambios en cualquier punto de vida del proyecto.  En lugar de esfuerzos para fijar todo desde el principio y luego controlarlo.  Adoptar las mejores prácticas de desarrollo aplicándolas dinámicamente durante el ciclo de vida del software.  Mejorar la calidad del software, la productividad y el tiempo de respuesta a cambios de requisitos por el cliente. “Los cambios de requisitos sobre la marcha son un aspecto natural, inevitable e incluso deseable del desarrollo de proyectos de software”
  20. 20. INTRODUCCIÓN A EXTREME PROGRAMMING COMPONENTES DE LA METODOLOGÍA Activities Values Principles Practices or Rules
  21. 21. INTRODUCCIÓN A EXTREME PROGRAMMING ACTIVITIES Coding Testing Listening Designing
  22. 22. INTRODUCCIÓN A EXTREME PROGRAMMING VALUES Simplicity Communication Feedback Respect Courage
  23. 23. INTRODUCCIÓN A EXTREME PROGRAMMING PRINCIPLES Rapid Feedback Assume Simplicity Incremental Change Embracing Change Quality Work
  24. 24. INTRODUCCIÓN A EXTREME PROGRAMMING PRACTICES OR RULES Planning • User Stories: User stories are written. • Release Plan: Release planning creates the release schedule. • Release Often: Make frequent small releases. • Iterative: The project is divided into iterations. • Iteration Planning: Iteration planning starts each iteration. Managing • Optimize Last: Give the team a dedicated open work space. • Steady Pace: Set a sustainable pace. • Stand-up Meeting: A stand up meeting starts each day. • Project Velocity: The Project Velocity is measured. • Move people around. • Fix XP when it breaks.
  25. 25. INTRODUCCIÓN A EXTREME PROGRAMMING PRACTICES OR RULES Designing • Simplicity •System Metaphor: Choose a system metaphor. •CRC cards: Use CRC cards for design sessions. •Spike Solution: Create spike solutions to reduce risk. • Nothing Early: No functionality is added early. • Refactor: whenever and wherever possible. Coding •Customer On-Site: The customer is always available. • Coding Standard: Code must be written to agreed standards. • Test Driven Development: Code the unit test first. •Pair Programming: All production code is pair programmed. • Serial Integration: Only one pair integrates code at a time. • Continuous Integration: Integrate often. •Continuous Integration: Set up a dedicated integration computer. • Collective Ownership: Use collective ownership.
  26. 26. INTRODUCCIÓN A EXTREME PROGRAMMING PRACTICES OR RULES Testing • Unit Tests: All code must have unit tests. • Unit Tests: All code must pass all unit tests before it can be released. • Bug Tests: When a bug is found tests are created. • Acceptance Tests: Acceptance tests are run often and the score is published. Test-Driven Development está incluido en el área de “coding”, no de “testing”
  27. 27. CARACTERIZANDO TDD DESDE XP CODING “Without code, there is no work product” SIMPLICITY “Keep it simple, stupid” (KISS) “You ain’t gonna needed it” (YAGNI) TESTING “If it's worth building, it's worth testing. If it's not worth testing, why are you wasting your time working on it?” DESIGNING “Using Test Driven Development – Refactoring” MANAGING AND PLANNING “SCRUM”
  28. 28. CARACTERIZANDO TDD DESDE XP  CODING  El único artefacto realmente importante es el código funcionando.  Las instrucciones que el ordenador interpreta.  El código claro y simple es la mejor fuente de documentación.  Para comprender, transmitir, explicar, evaluar y discutir una solución.  Es claro y conciso, se interpreta de solo una forma.  Código colectivo (acabar con “solo lo puede tocar el que lo hizo”).  SIMPLICITY  Codificar solo lo que realmente se necesita, lo que el cliente está pidiendo.  Se programa para el “ahora” y nunca para el “futuro” (*)  No se pierde tiempo anticipando el diseño de grandes frameworks o librerías.  Nunca se escribe una línea de código pensando en el “por si acaso luego se necesita” (al final no se necesitará).  Test-Driven Development será la herramienta de diseño.  El framework, las librerías y el código reutilizable aparecerán de forma orgánica.  Justo cuando se requieren y con un diseño modular y flexible al cambio.
  29. 29. CARACTERIZANDO TDD DESDE XP  TESTING  Tests unitarios utilizados para crear arquitectura, diseñar y codificar.  Tests de sistema o integración para aumentar y mantener la calidad.  Extreme: Si un poco de testing elimina defectos, mucho testing eliminará mas.  Los defectos, bugs, o errores son parte inherente del desarrollo de software.  Tantos tests como se puedan pensar, tantos como puedan romper el código, si todos los tests se pasan el desarrollo se ha terminado.  UNIT TESTS: DOCUMENTACIÓN  Una librería con documentación se entiende bien, pero si incluye ejemplos se entiende mucho mejor:  Los tests unitarios son ejemplos de cómo se usa nuestro código.  No sustituyen toda la documentación pero son una parte importante.  ACCEPTANCE TESTS: Especificación de requisitos ejecutable  Verificar que los desarrolladores han entendido los requisitos tal y como los quiere el cliente.  Ayudar al cliente a entenderse a si mismo.  Controlar el estado de avance del sistema.
  30. 30. CARACTERIZANDO TDD DESDE XP  DESIGNING  Diseños simples, claros, modulares y desacoplados.  Los módulos se mantienen inalterados si se modifican las dependencias.  Se diseña programando, mediante el refactoring usado en TDD.  TDD es el motor de diseño: simple, claro, modular, desacoplado, flexible a cambios y con mínimos defectos.  PLANNING & MANAGING  SCRUM: Ya estamos aplicando todos los principios XP de gestión y planificación de los proyectos.
  31. 31. YAGNI: “YOU AIN’T GONNA NEEDED IT” FUNDAMENTAL EN TDD (Y XP) Codificar para el “ahora” y nunca para el “futuro”, aunque parezca contraproducente, es clave tanto en Extreme Programming como en Test Driven Development.(*) Y a su vez Test-Driven Development es clave para poder programar el ahora y evolucionar hacia el futuro sin dificultades. La programación TDD permite evolucionar el producto “sin dolor”, hacia lo que quiere el cliente y hacia arquitecturas robustas, flexibles y de calidad (refactorización). “Evitar la anticipación NO SIGNIFICA crear spaghetti code(*)”· Ahora tenemos una metodología que nos permite obtener buenos diseños mediante la codificación directa. Diseñamos a través de una codificación que esta guiada y protegida por los tests.
  32. 32. TEST DRIVEN DEVELOPMENT Acceptance Test Driven Development Test-Driven Development = Test-First Development + Refactoring PRIMER VISTAZO GENERAL
  33. 33. TEST AFTER DEVELOPMENT TRADICIONALMENTE SE HAN CODIFICADO LOS TESTS DESPUÉS DE LA FUNCIONALIDAD ¿En serio tenemos esa tradición? ¿Cuántos habéis trabajado alguna vez en un proyecto en el que se programen tests? ¿Cuántos participáis en algún proyecto en el que se programen activamente los tests? ¿Qué tipos de tests y con que resultados?
  34. 34. AUTOMATED UNIT TESTS LOS TESTS SE TIENEN PROGRAMAR ¡¿QUEEE?!¿PROGRAMAR TESTS? ¿NO QUERÍAMOS SER AGILES? ¡TARDAREMOS EL DOBLE!
  35. 35. Una tarea terminada en 1 semana Hemos preparado la bomba de relojería Nada Funciona Realmente 3 semanas corrigiendo errores ¿Seguro? ¿O sólo hemos creado la falsa sensación de la tarea se ha terminado? ¿Qué hemos conseguido? Decir: ¡Ya está! ¡Y en una semana! “¿Hemos tardado realmente una semanas?” “¿Habríamos tardado más codificando pruebas unitarias?” AUTOMATED UNIT TESTS UN ESCENARIO HABITUAL “NO programar tests SI añade trabajo extra” “Y usando TDD obtendremos los tests unitarios de forma casi orgánica, a la vez que dirigen el desarrollo de la lógica funcional”
  36. 36. AUTOMATED UNIT TESTS YA HACEMOS PRUEBAS UNITARIAS Actualmente todo desarrollador ya realiza pruebas unitarias(*) ¿Qué tipos de pruebas unitarias estamos haciendo? PRUEBAS UNITARIAS MANUALES Y VISUALES (*) O esperamos que al menos esto sea cierto.
  37. 37. VISUAL UNIT TESTS DEPURACIÓN LÍNEA A LÍNEA REQUIEREN ESFUERZO Y TIEMPO ADICIONAL ¿Hemos ejercitado cada línea de código con el depurador antes de decir que está terminado? ¿Seguro? ¿Cómo podría estarlo? - depurando otra vez.
  38. 38. VISUAL UNIT TESTS VERIFICACIÓN VISUAL DE TRAZAS REQUIEREN ESFUERZO Y TIEMPO ADICIONAL ¿Aseguran las trazas que he ejercitado todo el código? ¿He revisado todas las trazas sin perder ninguna antes de decir que está terminado? ¿Seguro? ¿Cómo podría estarlo? – Traceando otra vez
  39. 39. VISUAL UNIT TESTS ASSERTS EN EL CÓDIGO FUNCIONAL REQUIEREN ESFUERZO Y TIEMPO ADICIONAL  Orientadas al establecimiento de pre-condiciones y pos-condiciones en depuración.  No orientadas al Unit Testing.  Se deben activar antes de ejecutar la aplicación.  Detienen la ejecución del sistema ante el primer fallo.
  40. 40. VISUAL UNIT TESTS REQUIEREN ESFUERZO Y TIEMPO ADICIONAL ¿No sería mejor dedicar este mismo tiempo a su automatización? No obstante veremos que usando TDD ese tiempo se optimiza y se funde de forma natural con la codificación de funcionalidad, pero:  ¿Estamos seguros que todos hacen pruebas visuales y que se hacen bien?  Las programación de pruebas unitarias elimina esa incertidumbre.  Repetir las pruebas unitarias programadas es hacer un sólo clic.  Repetir las pruebas visuales es volver a pasar todo el proceso manual que supone.  Los tests visuales son propensos a fallos y descuidos.  El ojo tiene que localizar los errores.  Mantenimiento: el resto de desarrolladores no sabrán qué probar, ni cómo.  Ausencia de pruebas de regresión: al modificar una funcionalidad ¿seguirá funcionando el resto?
  41. 41. AUTOMATED UNIT TESTS ¿SE ESTÁN USANDO?  En el panorama Java Open Source prácticamente todos los proyectos tienen Unit Tests completos.  Entiendo que en muchas otras tecnologías se da el mismo caso.  Algunos ejemplos: (*)  Tomcat  Glassfish (también usan integración continua con Hudson)  Spring Framework  Funambol SyncML Server (*) Desconozco si usan TDD. Todos estos proyectos tienen código de tests. Los desarrolladores están obligados a incluirlos. (*) Antes de seguir: Exercise 1 – My First Unit Test.
  42. 42. TEST DRIVEN DEVELOPMENT BUENO, YA BASTA DE BONDADES Y PROMESAS VEAMOS TEST DRIVEN DEVELOPMENT EN ACCIÓN
  43. 43. TEST DRIVEN DEVELOPMENT REFERENCIAS  http://www.agiledata.org/essays/tdd.html  Buena introducción de TDD.  Clean Code: A Handbook of Agile Software Craftsmanship, 2008, Robert C. Martin  Imprescindible  The Clean Coder: A Code of Conduct for Professional Programmers, 2011, Robert C. Martin  Diseño Ágil con TDD, 2010, Carlos Blé Jurado  Test Driven Development: By Example, 2003, Kent Beck  Del creador de Extreme Programming y Test Driven Development.  Test-Driven Development: A Practical Guide, 2003, David Astels  http://www.xprogramming.com/software  Software, herramientas, librerías para multitud de lenguajes (xUnit, mocks, integración,…), entornos de desarrollo, etc.
  44. 44. TEST DRIVEN DEVELOPMENT PRIMER VISTAZO AL CICLO ITERATIVO DE TDD One Small Scaled Test  Cada iteración no más de diez modificaciones en el código.  Diez líneas de código nuevas.  Diez correcciones en código existente.  Diez modificaciones para refactorizar.  …
  45. 45. TEST DRIVEN DEVELOPMENT PRIMER VISTAZO AL CICLO ITERATIVO DE TDD  CODIFICAR UN TEST  NO se codifican todos los posibles tests por adelantado.  Se codifica un solo test de una funcionalidad (small scaled test)  Ej: Si un solo método requiere N tests, se itera de forma completa el ciclo TDD uno por uno.  Son ejemplos de uso o clientes del código funcional.  Cada iteración completa debe suponer un incremento a pequeña escala, de unas diez modificaciones (líneas nuevas, actualización de la existentes, etc.).  ¿COMO SE CODIFICA EL TEST?  Debe cumplir las propiedades F.I.R.S.T  Si tardo dos minutos en una iteración (algo normal), habré ejecutado los tests 240 veces en un solo día.  Small Scaled Test: Test Unitario Aislado o Test de Integración de Lógica.  Obtener los tests: ¿Qué probaría si tengo que depurar?.  Obtener los tests: Aceptance Test Driven Development (el interruptor que arranca el motor TDD). (*) Exercise 1.4 – Test Code Coverage
  46. 46. TEST DRIVEN DEVELOPMENT PRIMER VISTAZO AL CICLO ITERATIVO DE TDD  EJECUTAR EL TEST  MUY IMPORTANTE: Se debe ejecutar antes de codificar la funcionalidad.  Probamos que realmente falla. Si no falla revisamos si el test es correcto.  No saltarse nunca este paso: que se tiende a olvidar.  Aunque en la primera iteración el código ni siquiera compilará, en las siguientes iteraciones no será así.  Seguimos refinando código que ya funciona.  Se ha codificado la funcionalidad que arregla el test anterior.  Implementamos el siguiente test este código que ya funciona y comprobamos que el test falla para asegurar que se ha implementado bien.  CODIFICACIÓN Y EJECUCIÓN (SUBITERACIONES)  Se codifica la funcionalidad hasta que el test funciona. “If the light is green, the code is clean” *JUnit slogan.
  47. 47. TEST DRIVEN DEVELOPMENT PRIMER VISTAZO AL CICLO ITERATIVO DE TDD  CODIFICANDO LA FUNCIONALIDAD  MUY IMPORTANTE: Se codifican única y exclusivamente las líneas que hacen pasar el test (al desarrollador le cuesta acostumbrarse).  No debemos codificar algo por si acaso luego se usa (“nunca se usará”).  No debemos entretenernos en diseñar y codificar un framework inicial (“al final habrá que cambiarlo”).  Arquitecturas, diseños, frameworks, librerías: aparecerán de forma natural. “The only thing that really matters in software development is working code” You ain’t gonna needed it” (YAGNI) “Keep it simple, stupid” (KISS) “If it's worth building, it's worth testing. If it's not worth testing, why are you wasting your time working on it?”
  48. 48. TEST DRIVEN DEVELOPMENT  CODIFICACIÓN Y REFACTORIZACIÓN (SUBITERACIONES)  Refactorizar comprobando que no se rompe el test que ya funcionaba.  FUNDAMENTAL: No se deja la factorización para el futuro, para momentos de baja carga de trabajo (“esos momentos no existen”).  Seguramente es el paso más importante de TDD  Refactorización después de cada pequeña iteración (constantemente).  La refactorización se aplica principalmente para:  Eliminar código repetido: No hay mayor aberración que la codificación “Copy & Paste”, que multiplica los esfuerzos de mantenimiento.  Extraer código reutilizable (XP: fijar estándares(*) en el grupo de desarrollo)  Refactorizamos guiándonos por los principios S.O.L.I.D. SIN REFACTORIZACIÓN NO HAY METODOLOGÍA SIN REFACTORIZACIÓN NO EXISTE TEST DRIVEN DEVELOPMENT (sólo sería una forma de Test First Development) (*) Estandarizar la reutilización: ver notas diapositiva
  49. 49. ACCEPTANCE TEST DRIVEN DEVELOPMENT PRIMER VISTAZO “Se podría decir que ATDD es una metodología de especificación de requisitos antes que una metodología de testing”  El cliente averigua como quiere realmente su sistema.  Se hace consciente de la casuística que tienen los casos de uso.  Los desarrolladores entienden lo que quiere el cliente.  El cliente ha visto toda la casuística y el desarrollador sabe como quiere que se resuelva cada variante.  Se codifican: Especificación de Requesitos Ejecutable.
  50. 50. ACCEPTANCE TEST DRIVEN DEVELOPMENT PRIMER VISTAZO  El cliente tiene feedback del avance del desarrollo mediante las pruebas de aceptación.  También llamadas “Pruebas de Cliente”: Asusta el compromiso de aceptar el producto sin condiciones si se pasan los tests.  La especificación de requisitos ya no se escribe como un redacción o “novela” de la funcionalidad.  User Stories (criterios de aceptación) descritos con ejemplos (tests)  El User Story equivale al título de un caso de uso tradicional.  El User Story se describe con un conjunto de ejemplos concretos y directos.  Se debe utilizar el vocabulario del negocio, y no de la implementación.  Se obtienen mediante colaboración del Analista del Negocio y el Product Owner (o cliente), y si es posible con la participación de los desarrolladores. Ejercicio 2: Starting Test Driven Development VOLVEREMOS CON ATTD MAS ADELANTE
  51. 51. TEST DRIVEN DEVELOPMENT TEST DRIVEN DEVELOPMENT EXPLAINED
  52. 52. TEST DRIVEN DEVELOPMENT INTRODUCCIÓN Test Driven Development es principalmente una metodología de desarrollo de software La misión principal de TDD es dirigir la arquitectura, el diseño y la codificación del software. El efecto colateral es que se termina obteniendo una batería completa de tests unitarios* y por tanto un software con pocos fallos. TDD no remplaza métodos tradicionales de testing, sigue siendo necesario el testing en niveles superiores (pero con menos bugs). * Veremos que los tests unitarios no son suficientes para validar un sistema.
  53. 53. TEST DRIVEN DEVELOPMENT INTRODUCCIÓN “The act of writing a unit test is more an act of design than of verification. It is also more an act of documentation than of verification. The act of writing a unit test closes a remarkable number of feedback loops, the least of which is the one pertaining to verification of function” Robert C. Martin* * Extreme Programming in Practice, Agile Software Development, y muchos libros más.
  54. 54. TEST DRIVEN DEVELOPMENT ESCALABILIDAD Se oye decir que las metodologías Agile sólo son válidas para pequeños proyectos de un puñado de personas durante unos pocos meses, que no funcionan en proyectos “reales” y más largos. SENCILLAMENTE… NO ES CIERTO Kent Beck, 2003 Un sistema Smalltalk, usando TDD, en un proyecto de 4 años, 40 personas/año. Resultando en 250.000 líneas de código funcional y 250.000 líneas de código de test. Se consiguieron 4000 tests ejecutándose en 20 minutos(*), con el sistema completo corriendo varias veces al día. Y existen testimonios similares de otros grandes proyectos. “It is clear that TDD works for good-sized systems” * Se refiere a grupo completo de test conseguidos, no los small scaled test usados en el día a día del desarrollo.
  55. 55. APLICANDO TESTS AL DESARROLLO TEST AFTER DRIVEN DEVELOPMENT (TAD) • Grupos dedicados a la programación de los tests. • Los desarrollos no tienen en cuenta la testeabilidad del diseño y el código funcional. • Util para la validación, verificación y detección de bugs, no para dirigir desarrollos. • Produce menor cobertura de código testeado. • Peligro de abandono de tests: no se mantienen, quedan obsoletos y se desactivan. TEST FIRST DEVELOPMENT (TFD) • Ciertos tests son difíciles de programar por adelantado. •Normalmente para tests muy alejados del código (sistema, integración, aceptación, etc.). • Más util para la detección de fallos que para dirigir los desarrollos (arquitecturas iniciales). • Peligro de abandonar los tests: distan del código, son más frágiles y requieren mayor mantenimiento. • Peligro de programar tests inútiles: El desarrollo posterior introduce cambios de diseño que invalidan los tests. TEST DRIVEN DEVELOPMENT (TDD) • Los tests son programados por los desarrolladores de la funcionalidad. • Están al mismo nivel que el código. • Produce un diseño y un código adaptado completamente al unit testing. • Dirige los desarrollos y proporciona cobertura completa de “tests unitarios”. • Los tests son parte del desarrollo de la funcionalidad: no se abandonan. • Programación en incrementos muy reducidos (small scaled tests). • Siguen siendo necesarios tests de más alto nivel para validar completamente el sistema.
  56. 56. TEST DRIVEN DEVELOPMENT FUNDAMENTOS Y OBJETIVOS  Sólo se implementa la funcionalidad necesaria, programando siempre el “ahora” y nunca el “mañana” (YAGNI).  Dirigir desde el código la arquitectura, diseño y codificación.  Minimizar el número de defectos que llegan a los tests de más alto nivel o a la plataforma de producción.  Producir software modular, altamente reutilizable y preparado para el cambio. “El desarrollador asume roles de arquitecto, diseñador y programador” La implementación de pequeños ejemplos en iteraciones constantes hace emerger la arquitectura que se necesita. Y se produce una arquitectura, diseño y código que emerge de la no ambigüedad de los tests automatizados.
  57. 57. TIPOS DE TESTS ALCANCE FINALIDAD TESTS DE SISTEMA TESTS DE INTEGRACIÓN TESTS UNITARIOS VISIBILIDAD TESTS DE ACEPTACIÓN TESTS FUNCIONALES TESTS NO FUNCIONALES RENDIMIENTO STRESS CARGA . . . TESTS DE INTERFAZ DE USUARIO TEST DE CAJA BLANCA TEST DE CAJA NEGRA Definiendo un vocabulario común  Test visuales o programados.  Dificultades para su automatización.  ¿Es viable e incluso recomendable la automatización de todos los tipos?.
  58. 58. ALCANCE DE LOS TESTS  TESTS DE SISTEMA  Ejercitan el sistema desde los extremos (end-to-end).  Entran por la interfaz del usuario final y llegan hasta el extremo opuesto.  Ej. desde la Web hasta la Base de Datos.  Se ejecutan en la plataforma de pre-producción.  TESTS DE INTEGRACIÓN  Ejercitan varias unidades del sistema de forma conjunta, verificando la correcta colaboración entre dependencias.  Se ejecutan en la plataforma de pre-producción.  Aunque son aceptables otros entornos según las intenciones de las pruebas.  Múltiples combinaciones según las colaboraciones implicadas.  La frontera que transforma test unitarios en integración a veces es difusa.  Algunos test de integración se pueden usar en las iteraciones TDD (test de integración a pequeña escala).
  59. 59. ALCANCE DE LOS TESTS  TESTS UNITARIOS  Ejercitan una unidad de código de forma aislada.  Un test = un caso o ejemplo de uso de un método o función.  Se compilan y se enlazan con el código funcional.  Se ejecutan en el equipo de cada desarrollador.  También periódicamente en entornos de desarrollo con herramientas de integración continua.  Pertenecen a los desarrolladores (usando TDD no hay otra opción).  A veces no es claro cual debería ser el nivel de aislamiento.
  60. 60. FINALIDAD DE LOS TESTS  TEST DE ACEPTACIÓN O DE CLIENTE  Verifican que el software cumple con los requisitos de negocio.  Pertenecen al Product Owner o Cliente.  Definidos en el Sprint Planning (ATDD: Discusión y Destilación)  Todo el equipo (desarrolladores y testers incluidos)  Al menos el Product Owner ayudado por el analista del negocio.  Tests Funcionales y No Funcionales.  Son “Tests de Sistema”(*) ejecutados en entorno de pre-producción.  Ayudan al cliente a entenderse y a los desarrolladores a entender al cliente.  Seguimiento del grado de avance: Especificación de Requisitos Ejecutable TESTS DE CERTIFICACIÓN(**) Los Tests de Aceptación son parecidos a los tests de certificación definidos por un grupo de QA. En ATDD se usan como Tests de Cliente que proporcionan un Mecanismo de Especificación de Requisitos. * No suelen entrar por la IU, pero se suelen considerar tests de sistema. ** Certificación: ver notas.
  61. 61. FINALIDAD DE LOS TESTS  TEST DE INTERFAZ DE USUARIO  Verifican la interfaz de los usuarios finales a todos los niveles.  Son test de sistema, ejecutados en entorno de pre-producción.  Aspecto, usabilidad, interactividad, interacción H-M, flujo, textos, faltas de ortografía, ausencia de fallos, tiempos de respuesta, legislación (ej. accesibilidad de la web, LSSICE), etc.  TEST DE ACEPTACIÓN vs INTERFAZ DE USUARIO  Los tests de aceptación no tienen por qué ejercitar la UI.  Validan la lógica de negocio o funcionalidad del sistema.  Verifican QUÉ hace el sistema, NO CÓMO se usa.  Dirigir los desarrollos mediante test de UI puede producir una arquitectura o diseño demasiado acoplado a una UI específica.  La funcionalidad debería ser completamente independiente de la UI.  Permitir el cambio de la UI o tener varias UI sin afectar a tests de aceptación.  La conexión entre la lógica y la UI debería provocar pocos fallos si se ha conseguido una buena cobertura de tests unitarios o de integración.  Incluir los tests de UI como tests de aceptación si es requisito de negocio.
  62. 62. FINALIDAD DE LOS TESTS  TESTS FUNCIONALES  Tests de aceptación que verifican los requisitos funcionales.  Son tests de sistema ejecutados en entorno de pre-producción.  TESTS NO FUNCIONALES  Tests de aceptación que verifican requisitos no funcionales.  Existen varios tipos de tests no funcionales:  Carga, stress, rendimiento (ej. número de altas por minuto), tolerancia a fallos (HA, High availability), etc.  Los puede definir un grupo de QA encargado de asegurar los requisitos mínimos de calidad de toda la organización.  El cliente puede imponer sus propios requisitos no funcionales.
  63. 63. TIPOS DE TESTS Aceptación Sistema UI Unitario Integración Unitario Integración
  64. 64. TEST UTILIZADOS EN TDD LO QUE NO ES TDD ¿Cuántos tests habría que codificar antes de la funcionalidad? TESTS DE INTERFAZ DE USUARIO TESTS FUNCIONALES TESTS NO FUNCIONALES TESTS DE INTEGRACIÓN TESTS UNITARIOS CODIFICAR TEST DE SISTEMA O INTEGRACIÓN  Habría que codificar innumerables tests antes de escribir una sola línea de código funcional.  Existen multitud de combinaciones de integración (qué con qué).  Extreme Programming precisamente quiere reducir la carga de trabajos previos, buscando la inmediatez. ¡Uf! ¿Repetir todo por cada iteración? * Ver notas
  65. 65. TEST UTILIZADOS EN TDD SPRINT PLANNING: Planificar Tests TESTS DE ACEPTACIÓN (ATDD) FUNCIONALES NO FUNCIONALES INTEGRACIÓN CARGA Se deciden los tests a realizar  ATDD: deciden siguiente funcionalidad. Entrada de datos del bucle TDD.  Grupo de QA: Certificación, test que aseguran la calidad de los productos.  Visuales, automáticos, antes, después, etc.  Valorar recursos y esfuerzos de mantenimiento. Desarrollo Incremental (minuto a minuto)  Todos utilizan TDD para los desarrollos.  Small Scaled Test: Se van desgranando tests de aceptación en test unitarios o de integración.  Los tests se mantienen y se desarrollan de forma paralela a la funcionalidad.  Los tests dirigen el desarrollo y las siguientes iteraciones deciden nuevos tests. * Ver notas INTERFAZ DE USUARIO SISTEMA … DEVELOPMENT: Desarrollar con TDD
  66. 66. DISTRIBUCIÓN DE LOS TESTS TRADITIONAL TESTING PYRAMID Invertir más esfuerzos en test de alto nivel y dejar el código con pocos o ningún test unitario. SYSTEM, GUI, ACCEPTANCE TESTS INTEGRATION TESTS UNIT TESTS PIRÁMIDE INESTABLE  Los test de alto nivel pesan demasiado, la pirámide se desmorona fácilmente.  La mayor carga está en los test más frágiles y más dependientes del entorno.  Esta aproximación que requiere esfuerzos específicos de desarrollo y mantenimiento.  Los tests quedan obsoletos fácilmente y se corre el riesgo de abandonarlos.
  67. 67. DISTRIBUCIÓN DE LOS TESTS MIKE COHN’S TESTING PYRAMID Small in number GUI (Test de UI y de Sistema) TESTS At least one per story SE INTENTA EVITAR EXCESIVA REDUNDANCIA DE TESTS (Test Funcionales, No Funcionales, Integración y Sistema) Loads of them, per method ACCEPTANCE TESTS UNIT TESTS (Test Unitarios Aislados, Test de Integración de Pequeña Escala) Automated tests were considered expensive to write and were often written months, or in some cases years, after a feature had been programmed. One reason teams found it difficult to write tests sooner was because they were automating at the wrong level. An effective test automation strategy calls for automating tests at three different levels, as shown in the figure, which depicts the test automation pyramid. •Mike Cohn. Uno de los fundadores de la Scrum Alliance. Profesor en el first Certified ScrumMaster course,. Varios libros publicados sobre Agile. * http://blog.mountaingoatsoftware.com/the-forgotten-layer-of-the-test-automation-pyramid •http://www.scrumalliance.org/
  68. 68. PROPIETARIOS DE LOS TESTS PRODUCT OWNER PRODUCT MANAGER SCRUM MASTER • Aceptación o Cliente • Interfaz de Usuario • Funcionales • No Funcionales DEVELOPER GROUP • Desarrolla con TDD • Unitarios • Integración QA GROUP • Sistema • Interfaz de Usuario • Certificación • Calidad • Funcionales • No Funcionales
  69. 69. RUNNING TDD ITERATIVO E INCREMENTAL  Cada iteración del bucle un incremento muy reducido.  Unas 10 modificaciones (del código) en cada iteración.  Objetivo: avanzar de forma paralela tests y funcionalidad.  No todos los tests por adelantado (si no en incrementos) RAPID FEEDBACK La pequeña escala de las iteraciones permite tomar decisiones de diseño y arquitectura lo antes posible para aplicarlas durante la refactorización.
  70. 70. RUNNING TDD 1 - ADD A TEST  Seleccionar un test de aceptación  Incrementalmente se descompone en “Small Scaled Tests”  Test Unitarios Aislados o Tests de Integración de Pequeña Escala.  Elaboramos nuestro propio “Unit Test Backlog”  Nuestra lista informal de tests para las N siguientes iteraciones.  Small Scaled Test  Término extraído de “Test Driven Development: By Example “, Kent Beck.  Recomendable utilizar Tests Unitarios Aislados.  En ocasiones más sencillo no aislar ciertas partes. Realizar el test unitario con las dependencias reales (integración de pequeña escala).  Valorar en función de cuánto nos alejamos de las propiedades F.I.R.S.T  Tests que deben cumplir las propiedades F.I.R.S.T (test unitarios).
  71. 71. F.I.R.S.T Son propiedades que deben cumplir los tests utilizados en el bucle TDD, y que no aplican a otros tipos de test como los test de integración y los test de sistema. FAST INDEPENDENT REPEATABLE SMALL TRANSPARENT
  72. 72. F.I.R.S.T FAST  Cada test unitario se debe ejecutar en milésimas de segundo.  Se ejecutan mientras desarrollamos en pequeños incrementos.  2 minutos por iteración: 240 ejecuciones en 8 horas.  Los tests de integración (o alto nivel) son demasiado lentos.  Hay que lanzar todos los test del modulo o unidad en desarrollo.  Si se vuelven lentos: separar las baterías de test.  Tests usados en el desarrollo y lanzados en cada iteración.  Tests a largo plazo (antes de un commit o integración).  Los “Tests Runners” pueden lanzar tests de una sola clase o un solo método.  Existen IDEs o plugins que ejecutan los tests automáticamente mientras estamos codificando.  Cuanto más veloz mejor: el test es lo que nos permite avanzar.
  73. 73. F.I.R.S.T INDEPENDENT  Cada test unitario completamente independiente del resto de tests.  Nunca predeterminar un orden en la secuencia de tests unitarios.  Un test es una unidad muy pequeña.  Ej: un método de una clase de test de xUnit.  Cada método de test se debe ejecutar independiente del resto.  Las librerías tipo xUnit fomentan la independencia de los tests.  No deben depender de configuraciones de despliegue.  No depender del acceso a una base de datos concreta.  No depender de datos existentes.  No depender de acceso a máquinas remotas concretas.  …  Debe ser posible su ejecución en cualquier máquina en cualquier estado. * Los test de más alto nivel que no se usan en TDD no tienen por qué cumplir estas propiedades. Por ejemplo un test de IU que necesita probar una secuencia predeterminada del flujo de eventos, o tests de una máquina de estados.
  74. 74. F.I.R.S.T REPEATABLE  Un test unitario debe ser inocuo.  No debe alterar el sistema.  Debe comportarse como si nunca se hubiera ejecutado.  No alterar datos de producción, no enviar emails, mensajes, etc.  Debe producir siempre el mismo resultado.
  75. 75. F.I.R.S.T SMALL  Probar la mínima cantidad de funcionalidad posible.  Deben ser atómicos, probar lo indivisible.  Prueban un solo caso de un método de una clase.  Permitir localizar más rápidamente los defectos.  Evitar el tener que usar el depurador para encontrar la causa del fallo.  Idealmente cada test debería tener un solo assert.  En ocasiones se puede relajar.  Ej. tests de la misma funcionalidad.
  76. 76. F.I.R.S.T TRANSPARENT  Debe estar claro cual es el propósito de cada test.  Debe funcionar como documentación.  Ejemplo de uso de la API que estamos testeando.  Ejemplo de uso del Object Under Test o Subject Under Test.  SIMPLICITY: debe ser sencillo, simple y fácil de leer.  Un test complejo es indicio de que el diseño está fallando.  Si un test se complica: plantearnos una refactorización.  Un test debería quedar claro incluso sin usar comentarios.
  77. 77. TESTS UNITARIOS: OTRAS CARACTERÍSTICAS  El Object Under Test NO debe tener código de test.  Aunque sí se recomiendan adaptaciones que faciliten su testing.  Pero la verificación (assert) siempre debe ser externa.  Los tests usados en TDD nunca deben cruzar fronteras.  Ni comunicación entre maquinas ni entre procesos.  Dependencias externas convierten tests unitarios en tests de integración.  Si fallan las dependencias será complicado encontrar el fallo.  Peor cuanto más extensa sea la cadena de dependencias.  Evitar el uso del depurador: aislando dependencias.  ¿Qué convierte un test unitario en test de integración?  Habitualmente no se consideran librerías del sistema o de terceros.  Intentar el aislamiento entre módulos del sistema en desarrollo.  El uso de dependencias reales puede simplificar los tests.  El desarrollador debe valorar (dando preferencia a test aislados).
  78. 78. RUNNING TDD 2 - RUN THE TEST  El test debe fallar: la funcionalidad aun no se ha codificado.  IMPORTANTE: evitar escribir falsos tests o código no probado.  En la primera iteración el código ni siquiera compilará.  Pero en siguientes iteraciones se estará modificando código existente.  Es necesario asegurar que falla.  Este paso se tiende a olvidar: NO SALTÁRSELO NUNCA (*)  Cuando el test no falla.  Revisar la implementación del test y del código funcional.  Violación YAGNI:  Funcionalidad anticipada en una iteración anterior.  Se había codificado más de lo necesario.  Funcionalidad implementada sin su correspondiente test.  Peligro de acabar con código funcional no testeado. * La importancia de hacer fallar los tests: ver notas.
  79. 79. RUNNING TDD 3 – MAKE A LITTLE CHANGE  CODIFICAR  Se codifica sólo lo necesario para satisfacer el test.  Programar el “ahora” no el “mañana” (YAGNI).  NO ANTICIPAR FUNCIONALIDAD  Evitar el “por si acaso luego se necesita” (al final nadie lo usará).  Más productivo dejar para mañana lo que se necesita mañana.  La funcionalidad anticipada al final habrá que modificarla. “El esfuerzo de futuras modificaciones será menor que el esfuerzo de haber anticipado una funcionalidad, arquitectura o un diseño”
  80. 80. RUNNING TDD 3 – MAKE A LITTLE CHANGE  SIEMPRE DEBE EXISTIR UN TEST QUE FALLA  Codificando la solución de un test pueden aparecer nuevas necesidades.  NO SE CODIFICAN AHORA  Se apuntan en el “Unit Test Backlog” para la siguiente iteración.  Por trivial que sea: no se codifica algo sin su correspondiente test.  Se corre el riesgo de dejar código sin cobertura de test.  FLEXIBILIDAD vs ANTICIPACIÓN  Es fácil adaptarse a cambios de requisitos gracias a la cobertura de tests.  La refactorización hará emerger incrementalmente la arquitectura o el diseño flexible, desacoplado y modular que se persigue.  COLECTIVIDAD DEL CÓDIGO  Deja de existir el “no lo voy a tocar por si acaso”.  Todos los desarrolladores pueden cambiar todo.  Los test unitarios avisarán si algo deja de funcionar.
  81. 81. CÓDIGO AUTO-EXPLICATIVO  MINIMIZACIÓN DE COMENTARIOS  Las prácticas Agile (XP o TDD) recomiendan la minimización de comentarios.  El código se evoluciona, pero los comentarios suelen acabar obsoletos.  Los comentarios no suelen tener mantenimiento: al final acaban desfasados.  Minimización, no eliminación.  Los comentarios siguen siendo importantes y deben seguir existiendo.  Comentarios referidos a la semántica que no cambia.  Responsabilidades de módulos, clases o métodos.  Ej. JavaDocs, comentarios en las cabeceras de métodos, clases, etc.  NUNCA dejar en forma de comentario el código obsoleto.  Si el código no sirve se elimina.  Si el código eliminado era necesario los tests avisarán.  La herramienta de SCM (Software Configuration Management) permite volver hacia atrás en cualquier momento.
  82. 82. CÓDIGO AUTO-EXPLICATIVO  CÓDIGO SENCILLO, CLARO Y LIMPIO  Si hacen falta muchos comentarios, la solución se ha complicado.  Revisar la implementación o el diseño y refactorizar.  IDENTIFICADORES O SÍMBOLOS  Identificadores significativos y auto-explicativos (mejor que comentarios)  Un nombre de clase, atributo, método, variable, etc. lo debe decir todo, no importa lo largo que sea.  Los IDEs actuales evitan tener que teclearlos (autocompletar).  La longitud no afecta al rendimiento del código compilado.  TESTS UNITARIOS COMO DOCUMENTACIÓN  Son ejemplos de cómo se usa nuestro código.  Deben ayudar a entender el código mejor que cualquier comentario.  La documentación describe una API, pero los ejemplos la clarifican.  Complementan la documentación, no la sustituyen. (*) Ejemplos de código autoexplicativo: Proyectos OpenSource, se entienden perfectamente casi sin comentarios.
  83. 83. RUNNING TDD 4 – RUN THE TEST  Se continua ejecutando el test y codificando la funcionalidad.  Hasta que la funcionalidad de la iteración actual se completa.  Si se encuentra un bug no relacionado con la iteración actual:  Se debe implementar un tests antes de corregirlo.  El test debe reproducir el bug encontrado y fallar.  La funcionalidad se codifica para corregir el test fallido.  Reparar los bugs sin codificar los tests: dejará código sin probar.
  84. 84. RUNNING TDD 5 – REFACTOR Modificar diseño e implementación sin alterar comportamiento.  Se plantea si es necesaria una refactorización:  Al finalizar y al empezar cada iteración.  REFACTORIZAR PARA:  Eliminar código repetido, arreglando el “copy & paste” realizado.  Extraer código reutilizable en librerías comunes.  Producir código legible, mantenible y extensible (funcionalidad y tests).  Mejorar arquitectura, diseño y codificación.  La refactorización es incremental: NO SE APLAZA.  Refactorizar el código funcional y el código de los tests.  Refactorización guiada mediante aplicación de los principios S.O.L.I.D.(*) SIN REFACTORIZACIÓN NO EXISTE TEST DRIVEN DEVELOPMENT
  85. 85. S.O.L.I.D. Principios básicos de programación y diseño orientado a objetos cuya aplicación conjunta hace más probable la construcción de sistemas fáciles de extender y mantener. SINGLE RESPONSIBILITY PRINCIPLE OPEN/CLOSED PRINCIPLE LISKOV SUBSTITUTION PRINCIPLE INTERFACE SEGREGATION PRINCIPLE DEPENDENCY INVERSION PRINCIPLE (*) Introducidos por Robert C. Martin a principios del 2000.
  86. 86. S.O.L.I.D SINGLE RESPONSIBILITY PRINCIPLE (SRP)  Un objeto debe tener una única responsabilidad, que debe estar enteramente encapsulada en una clase.  Toda la funcionalidad proporcionada por una clase debe estar estrechamente alineada con su responsabilidad.  Por extensión el mismo principio se aplica a los métodos.  Una clase o método deben resolver una sola problemática.  Una única responsabilidad = Una única razón para ser modificada.  En TDD refactorizamos para extraer responsabilidades.  Ejemplo:  Un módulo que genera, formatea e imprime un informe.  Puede haber dos razones para modificarlo.  Cambiar el contenido o cambiar el formato.  Son dos responsabilidades, lo que implica dos clases.
  87. 87. S.O.L.I.D OPEN/CLOSED PRINCIPLE (OCP)  Las entidades de software (clases, módulos, métodos, etc.) deben estar abiertas a extensiones pero cerradas a modificaciones.  Permitir cambios en su comportamiento sin necesidad de alterar su código fuente.  Varias formas de resolverlo.  Herencia  Heredando se pueden sobreescribir métodos que cambian el comportamiento.  Pero la clase está cerrada: se ha finalizado su responsabilidad.  Se puede ampliar o modificar la funcionalidad heredando y sobreescribiendo.  Interfaces o clases abstractas  Dependencias sobre entidades sin implementación.  Se puede sustituir la implementación, cambiando el funcionamiento de una colaboración sin afectar a los clientes de la interfaz.  Evitar construcciones del lenguaje que cierran a modificaciones.  Ej. métodos o clases “final” en Java.
  88. 88. S.O.L.I.D LISKOV SUBSTITUTION PRINCIPLE (LSP)  Define la semántica de la relación “subtipo”.  Si q(x) es una propiedad demostrable sobre un objeto de tipo T. Entonces q(y) debe ser cierto para objetos de tipo S cuando es un subtipo de T.  Garantiza la semántica de interoperabilidad entre los tipos implicados en una jerarquía (semántica de la herencia).  Si un método acepta un tipo T, debe funcionar correctamente con objetos de cualquier tipo heredado de T.  El código que funciona sobre clases base debe seguir funcionando sobre clases heredadas.  Evita la violación de la semántica de las clases heredadas. Si una clase heredada modifica la semántica de la clase base, se empezarán a obtener fallos inesperados (normalmente a largo plazo) al usar los objetos en métodos creados para objetos de la clase base.
  89. 89. S.O.L.I.D LISKOV SUBSTITUTION PRINCIPLE (LSP)  Típico ejemplo de violación LSP.  Clase “Cuadrado” heredando de clase “Rectángulo”.  Un cuadrado también tiene ancho y alto, aunque con el mismo valor.  El constructor de la clase cuadrado iguala ambos valores. El método “ZoomToFit” trabaja sobre rectángulos y puede modificar las propiedades “length” y “witdh” de forma independiente. Por herencia “ZoomToFit” acepta “cuadrados”. El método desigualará los lados del “cuadrado”, rompiendo la semántica de la clase (o la postcondición de igualdad de los lados).
  90. 90. S.O.L.I.D INTERFACE SEGREGATION PRINCIPLE (ISP)  Evitar el diseño de interfaces extensas de propósito general.  Si una interfaz (o métodos públicos de una clase) ha crecido demasiado existirán muchos objetos que sólo usan una parte de la interfaz.  Será necesario dividir en varias interfaces más pequeñas y específicas.  Mejor pequeñas interfaces específicas que una sola de propósito general.  Los clientes de la interfaz (API) solo deben depender de lo que realmente utilizan.  Ningún objeto debe ser forzado a depender de métodos que no utiliza.  Un solo jugador no puede estar molestando el resto del equipo.  Si una interfaz es extensa surgirán muchos clientes que solo necesitan una parte de la interfaz. Indirectamente se estarán acoplando los objetos a la interfaz completa y tendrán más posibilidades de ser afectados por cambios en la interfaz.
  91. 91. S.O.L.I.D DEPENDENCY INVERSION PRINCIPLE (DIP)  Los objetos deben depender de interfaces en lugar de implementaciones.  Los objetos no crean o buscan las instancias de sus dependencias.  Los objetos permiten que las dependencias se configuren de forma externa. APLICANDO DIP EJEMPLO GlobalAddressBook está fuertemente acoplada a la clase RadomIdGenerator. Depende de una implementación específica y debe crear o buscar la instancia utilizada. EJEMPLO CommandLineUI será encargado de crear las instancias y configurar las dependencias, que sólo están basadas en interfaces.
  92. 92. S.O.L.I.D DEPENDENCY INVERSION PRINCIPLE (DIP)  Principio para el desacoplamiento de dependencias.  Las relaciones de dependencia establecidas desde los niveles más altos a los más bajos de invierten(*).  High-level modules should not depend on low-level modules. Both should depend on abstractions.  Abstractions should not depend on details. Details should depend on abstractions.  Inversion of Control (IoC): Normalmente Direct Injection (DI).  IMPORTANTE: Para usar IoC no es necesario ningún framework.  Es un patrón de diseño, no hace falta Spring, JDK7, Plexus, etc.  La colaboración se basa en interfaces, y la implementación de las interfaces se entrega de forma externa. IoC: Hollywood Principle “Don’t call us, we’ll call you” * Definición formal de DIP: la abstracción utilizada suele ser una Interfaz. Una buena explicación en: http://www.oodesign.com/dependency-inversion-principle.html
  93. 93. S.O.L.I.D DEPENDENCY INVERSION PRINCIPLE  OBJETIVO: Dividir responsabilidades usando acoplamiento débil.  SRP: Responsabilidades que se pueden extraer en forma de nuevas clases.  DIP: Las dependencias se gestionan aplicando el principio de inversión.  REFACTORIZANDO 1. Localizar una responsabilidad que se puede extraer como una nueva clase. 2. Crear una interfaz para la nueva clase (sólo usará otras interfaces). 3. Las clases sólo dependerán de la interfaz (depender de especificación y no de implementación). 4. Los objetos no crean o buscan sus dependencias, no es su responsabilidad. 5. Habilitar la configuración externa de dependencias (Direct Injection, constructores, setters, getters, etc.). 6. Los clientes, contenedores o marcos de trabajo crean las instancias y configuran las dependencias.
  94. 94. S.O.L.I.D DEPENDENCY INVERSION PRINCIPLE  Permite crear un diseño altamente desacoplado.  Cambiar la implementación de las dependencias sin modificar o recompilar el código fuente, incluso por configuración en la plataforma de despliegue.  PROPORCIONA UN DISEÑO:  Modular: Simplifica mantenimientos. El código se modifica en clases simples y enfocadas en una sola responsabilidad.  Flexible: Modificar una dependencia o cambiar la implementación utilizada en niveles superiores no afecta ni al código fuente ni al compilado.  Configurable: Permite configurar las dependencias de forma externa, incluso en la plataforma de producción, sin recompilar el código.  Acoplamiento Débil: de módulos, unidades o clases.  EFECTO LATERAL: Facilita el aislamiento de los Unit Tests.  Test aislados mediante la inyección de Mocks en el Object Under Test.  Podemos aislar el test unitario de sus dependencias reales o dirigir su ejecución (como si estuviéramos depurando con datos de prueba).
  95. 95. S.O.L.I.D DEPENDENCY INVERSION PRINCIPLE (DIP) CommandLineUI crea o busca las instancias concretas de la implementación de cada interfaz. Puede configurar la colaboración en tiempo de despliegue, seleccionando y configurando las dependencias. Se pueden combinar diferentes implementaciones de GlobalAddressBook para ofrecer por ejemplo diferentes niveles de caché. La clase CommandLineUI puede ser un contenedor genérico capaz de leer desde un fichero la configuración de dependencias e implementaciones para un despliegue concreto.
  96. 96. DISEÑO Y ARQUITECTURA ¿CUANDO ESTAMOS DISEÑANDO CON TDD?  Principalmente durante la refactorización de cada iteración.  ANTICIPARSE A LA REFACTORIZACIÓN.  La refactorización no implica hacer las cosas mal para luego corregirlas.  Durante la codificación de la funcionalidad (después del test) podemos directamente implementar buenos diseños, si los hemos identificado.  Si se identifican responsabilidades se implementan directamente separadas.  Las colaboraciones se implementan directamente aplicando DIP.  Se evita el copy & paste con métodos privados o clases reutilizables.  …  Los ejercicios del curso muestras un avance muy granular, para ejercitar los principios de la refactorización.  Cuanto más rápido se trabaje y con mejor calidad mejor.  Sin olvidar que no hay que anticipar semántica y funcionalidad.  Sin olvidar que no se codifica ni una línea de código sin su test.
  97. 97. TEST DRIVEN DEVELOPMENT UNIT TESTING FRAMEWORKS
  98. 98. FASES DE UN TEST UNITARIO  SET UP  Preparar los Test Fixtures (REPEATABLE).  Instanciar el Object Under Test.  Preparar Test Objects (datos de ejemplo que se usan en los tests).  EXERCISE  Interactuar con el Object Under Test (ejecutar el código a testear).  VERIFY  Determinar que ha sido obtenido el resultado esperado.  TEAR DOWN  Limpiar (INDEPENDENT) o desmontar los Text Fixtures. FIXTURE: Estado predeterminado que fija la base de los test que se ejecutarán.  Crear un conjunto de datos de prueba determinado (ej. base de datos en tests de integración).  Preparar y configurar el Object Under Test.  Preparar y configurar varios Test Objects.  Preparar y configurar Mocks, Stubs, Fakes, etc.  … * Fixture o Test Context
  99. 99. FASES EN LIBRERÍAS xUNIT  SET UP: setUp, Before, etc.  VERIFY: assertTrue, assertEquals, expectedException, etc.  TEAR DOWN: tearDown, After, etc. CLASS SET UP & CLASS TEAR DOWN (afterClass, beforeClass)  PELIGRO: Violación del principio INDEPENDENT (code smell).  Prepara un Fixture que se reutilizará en todos los tests.  Tener mucho cuidado, mejor evitarlo (dependencias entre tests).  Se suele encontrar en tests de integración lentos.  EJEMPLO: Tests de Integración para Data Access Objects.  Class Set Up / Class Tear Down: Crea y destruye las tablas de la base de datos de pruebas (preferible en memoria). Los tests serían lentos si se hiciera antes y después de cada test.  Set Up / Tear Down: begin/rollback transaction (INDEPENDENT). El rollback permite comenzar siempre con un estado determinado, con la base de datos vacía, con los datos del Fixture común o de cada test.
  100. 100. CICLO DE VIDA DE TESTS xUNIT Está diseñado para cumplir las propiedades F.I.R.S.T. Suele ser el mismo en todas las tecnologías o frameworks xUnit (JUnit, CppUnit, SUnit, PyUnit, JsUnit, etc.). EJEMPLO: Clase TestAddressBook (contiene 3 tests, un método cada test) // Ejecución (simplificada) que realizará JUnit // Crea todas las instancias, una para cada test, con sus propios atributos // independientes(INDEPENDENT) test1 = new TestAddressBook(); test2 = new TestAddressBook(); test3 = new TestAddressBook(); TestAddressBook.setUp() // Before Class test1.setUp() test1.testSomething() // Ejecución del test 1 sobre la instancia 1 test1.tearDown() test2.setUp() // Before Test (INDEPENDENT) test2.testAllThatStuff() // Ejecución del test 2 sobre la instancia 2 test2.tearDown() // After Test (REPEATABLE) test3.setUp() test3.testAnythingElse() // Ejecución del test 3 sobre la instancia 3 test3.tearDown() TestAddressBook.tearDown() // After Class * http://en.wikipedia.org/wiki/XUnit#xUnit_Frameworks (arquitectura librerías xUnit) * http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks (lista completa de librerías xUnit)
  101. 101. UNIT TEST CODE COVERAGE  Existe una teoría extensa de cobertura de tests (*)  Líneas de código funcional ejecutado.  Evaluación de expresiones en puntos condicionantes (branch coverage).  Herramienta para el seguimiento (SCRUM masters).  Herramientas para informes de cobertura: Cobertura, Quilt, Clover, etc.  Establecer los niveles mínimos de cobertura exigidos.  Decidir el código que se cubre con test de alto nivel en lugar de unitarios (QA).  Decidir código al que no se le exige unit testing.  No es necesario exigir 100% de cobertura en los tests.  Ej. propiedades respaldadas por atributo (generación automática, lenguajes con soporte directo de properties, etc.)  Descubrimiento de código funcional innecesario, el test no lo ejecuta debido a que el código sobra (a veces sucede en condicionantes).  En ocasiones mucho código repetitivo y simple no necesita tests.  Programando con TDD se consigue amplia cobertura de forma natural. * http://en.wikipedia.org/wiki/Code_coverage: Introducción y referencias sobre teoría de cobertura.de tests.
  102. 102. TEST DRIVEN DEVELOPMENT DUMMY / FAKE / STUB / MOCK / SPY
  103. 103. DUMMY / FAKE / STUB / MOCK / SPY Dependency is the key problem in software development at all scales…. Eliminating duplication in programs eliminates dependency. — Kent Beck, Test-Driven Development: By Example
  104. 104. DUMMY / FAKE / STUB / MOCK / SPY DUMMY  Se entrega como argumento pero nunca se utiliza.  Sirve para cubrir requerimientos de parámetros. FAKE  Exhibe una implementación completa pero simulada de un objeto.  Ej. HttpServletRequest que permite configurar parámetros de entrada, un DAO usando datos en memoria, etc. STUB  Respuestas prefijadas con argumentos de entrada específicos.  Carece de la implementación “real” que tiene un FAKE, tan sólo devuelve respuestas programadas para llamadas concretas.  Permite dirigir el comportamiento del Object Under Test para forzar la ejecución del código a testear.  Ej. searchContact(“Pedro”): Contact / searchContact(“Edu”): null
  105. 105. DUMMY / FAKE / STUB / MOCK / SPY MOCK  Evolución de un STUB que permite realizar verificación por interacción.  Definir expectativas en las llamadas a sus métodos.  Prueba si el Object Under Test utiliza las dependencias como se espera.  PELIGRO: No conviene abusar de la verificación por interacción.  Ej. verificar que únicamente se usa un método de la clase, que se llama N veces, que no se llama nunca, etc. SPY  Partial Mock.  Utiliza una implementación real modificando alguno de sus métodos.  Aplicando el principio Open/Closed (abierto a extensiones, cerrado a modificaciones) se puede heredar y sobrescribir los métodos simulados.  PELIGRO: Mejor evitarlo, síntoma de code smell.  Útil para tests unitarios dentro de Heavyweight Frameworks.  Frameworks rígidos no preparados para unit testing.  Suele ser necesario para modificar comportamientos heredados desde el framework por el Object Under Test (Ej. Servlets). (*) Mock: Se utilizará esta nomenclatura para nombrar de forma genérica todos los tipos.
  106. 106. DUMMY / FAKE / STUB / MOCK / SPY PROPÓSITO  AISLAR TEST UNITARIOS (*)  Para ajustar el test a las propiedades F.I.R.S.T.  Para evitar interferencias de las dependencias en el código testeado.  Una cadena extensa de dependencias dificulta la localización de los bugs.  El test unitario debería validar exclusivamente el código del Object Under Test.  Evitar que un test unitario falle por errores en las dependencias.  Fallos que deberían ser detectados en los tests unitarios de dichas dependencias.  Evitar el uso del depurador para localizar el origen de los fallos.  FORZAR EJECUCIÓN DE CÓDIGO  Forzar la ejecución del código que se quiere ejercitar en el test.  Evitar la dependencia sobre código con alta aleatoriedad (REPEATABLE).  Dirigir el comportamiento del Object Under Test en la ejecución del test.  VERIFICAR COLABORACIONES  Probar que el Object Under Test usa correctamente sus dependencias.  PELIGRO: La verificación por interacción se debe minimizar. * Aislamiento de Test Unitarios: ver notas
  107. 107. TIPOS DE VALIDACIÓN VALIDACIÓN POR ESTADO  Verificar que se devuelve el valor esperado o que el Object Under Test termina en un estado determinado (estado de las propiedades) después de la ejecución del test.  No se hacen suposiciones de cómo el Object Under Test utiliza sus dependencias.  Suele producir test de caja negra (menos frágiles).  Puede proporcionar menos cobertura de código testeado al no adaptarse a detalles específicos de la implementación.
  108. 108. TIPOS DE VALIDACIÓN VALIDACIÓN POR INTERACCIÓN  Verificar que el Object Under Test utiliza correctamente sus dependencias.  Suele producir test de caja blanca (más frágiles).  Revelan detalles de la implementación.  Permiten capturar los parámetros de las llamadas a las dependencias.  Posibilitando una forma de validación por estado en entornos poco adaptados al unit testing (Heavyweight Frameworks).  Usando mocks suele ser más sencillo idear validaciones por interacción.  Aunque conviene evitarlo ya que hacen el test más difícil de entender (menos claridad) y más acoplado a la implementación.  Los tests de caja blanca si son válidos en TDD (dirige el desarrollo).  Si el test se rompe tras cambios en la implementación, sólo hay que arreglarlo. En la mayor parte de los casos se puede convertir una validación por interacción en una validación por estado, programando la respuesta adecuada del stub.
  109. 109. VALIDACIÓN POR INTERACCIÓN PELIGROS Y RECOMENDACIONES EVITAR LA EXCESIVA VALIDACIÓN POR INTERACCIÓN EVITAR VERIFICACIONES DE INTERACCIÓN REDUNDANTES VER EJERCICIOS Y EJEMPLOS(*)  El desarrollo de la capa de lógica de negocio mediante TDD requiere test unitarios aislados de la capa de persistencia (DAOs).  Es necesario utilizar mocks/stubs de las dependencias (DAOs).  Es fácil caer en el error de programar los tests de la lógica de negocio como verificaciones rigurosas de toda interacción (o falta de interacción) con la capa de persistencia. * Ejercicios Mocking Avanzado: Los controladores de la web se comenzaron con test de interacción (lo más inmediato) y al final se acabaron convirtiendo en test de validación por estado (con menos código y más legibles). * Ejercicios Persistencia: implementación del servicio GlobalAddressBook basado en el uso de DAOs.
  110. 110. VALIDACIÓN POR INTERACCIÓN EVITAR LA EXCESIVA VERIFICACIÓN DE INTERACCIONES  Convertir validación por interacción en validación por estado.  La misión principal de un mock/stub es proporcionar datos de ejemplo para las dependencias del Object Under Test.  Misión del test unitario:  Únicamente verificar que la lógica del Object Under Test es correcta.  No debe verificar la lógica de las dependencias: antipatrón.  No debe verificar como utiliza sus dependencias, validando toda interacción o falta de interacción.  No es importante verificar que el resultado del código bajo prueba se produce utilizando ciertas dependencias.
  111. 111. VALIDACIÓN POR INTERACCIÓN TIPOS DE INTERACCIONES  Asking an object of data: El resultado del código bajo prueba se genera mediante datos devueltos por las dependencias.  STUBBING:  Las dependencias devuelven datos que permiten la ejecución del código.  Assert: Correcto si devuelve el valor esperado.  Una verificación por interacción sería redundante.  El verify sería una copia exacta de la programación del stub.  Si el test no falla el resultado habrá usado el stub: interacción implícita.  Telling an object to do something: El código bajo prueba no devuelve un resultado, su misión es invocar alguna dependencia.  MOCKING:  Las dependencias no devuelven datos, sólo ejecutan acciones.  Verify: Correcto si termina invocando la dependencia con los datos adecuados.  Un Test = Un único Assert o un único Verify.  El verify será el equivalente al assert, que ahora no podemos usar.  El verify es tan sólo un medio para realizar la afirmación. Ver: http://monkeyisland.pl/2008/04/26/asking-and-telling/
  112. 112. DEPENDENCIAS VS SUSTITUTOS ¿Cuándo utilizar objetos falsos o dependencias?  Lo debe decidir el desarrollador en función de la complejidad.  Intentar cumplir las propiedades F.I.R.S.T.  Usar las dependencias reales puede requerir menos trabajo.  Se convierten en test de integración, si la cadena se alarga serán más frágiles.  Pueden complicar la localización y corrección de los errores.  El objeto falso permite aislar y realizar verificaciones por interacción.  Pueden requerir más esfuerzo, pero aíslan de fallos de las dependencias.  La verificación por interacción acopla el test a la implementación.  El test unitario no realiza pruebas de integración (lo que se persigue en TDD).  RECOMENDACIÓN  Usar el objeto real si la cadena de dependencias no es muy profunda.  Si cumple FIRST aunque sea de integración: no modifica datos, rápido, etc.  Usar las dependencias si el uso de mocks supone mucho código de test.  Mocks: más fácil cuanta más experiencia, las librerías lo convierten en un juego.
  113. 113. GENERACIÓN AUTOMÁTICA DE MOCKS  Las librerías de generación de mocks convierten la creación y uso de mocks en un juego.  Java: JMock, EasyMock, Mockito, etc.  Mockito: Simplicidad y documentación de pocas páginas (en XP y TDD la simplicidad siempre debe tener un peso importante).  Otros lenguajes: mockcpp, MockitoPP, OCMock, NMock, etc.  Facilidades: Todas las librerías de mocks en cualquier lenguaje o tecnología se suelen fundamentar sobre la misma teoría y paradigmas, se usan prácticamente igual. * http://en.wikipedia.org/wiki/List_of_mock_object_frameworks (lista de mock objects frameworks) * http://www.xprogramming.com/software (lista librerías y herramientas para XP y TDD)
  114. 114. GENERACIÓN AUTOMÁTICA DE MOCKS PELIGROS Y RECOMENDACIONES  Las librerías de generación de mocks y frameworks de unit testing suelen contener características muy avanzadas.  TDD y XP promueven la simplicidad: conveniente restringirse siempre a lo más básico (creación de tests simples y claros).  No hay que convertirse en un experto de las librerías (con dos o tres cosas es suficiente).  Más del 90% del unit testing se resuelve con menos del 10% de las facilidades de los frameworks.  La propia documentación de las librerías avisa de características peligrosas.  Facilidades que cubren cualquier posible necesidad (testing de código legacy).  Avisan que el uso de algunas facilidades (ej. spy) produce malos diseños (code smell).  Indicador de la necesidad de refactoring  Si los tests se complican o se necesitan características complejas de las técnicas o librerías de mocks, quizás se está avanzando hacia un mal diseño (del código funcional), indicador de que se necesita refactorizar.  SOLID: Separando responsabilidades, usando la inversión de dependencias, etc. se pueden obtener mejores diseños y más fáciles de probar.
  115. 115. TEST DRIVEN DEVELOPMENT TEST DRIVEN DEVELOPMENT APLICADO A ENTORNOS ESPECÍFICOS
  116. 116. CONTENEDORES / COMPONENTES  HEAVYWEIGHT FRAMEWORKS La mayoría de los frameworks “legacy” son rígidos, pesados y no están optimizados para el Unit Testing.  LIGHTWEIGHT FRAMEWORKS Actualmente la comunidad de desarrolladores propone y busca entornos ágiles basados en conceptos como:  Don’t Repeat Yourself (DRY).  Convention Over Configuration (CoC).  Lightweight Frameworks over Heavyweight Frameworks.  Plain Objects (clases simples débilmente acopladas con el framework).  EJEMPLOS EN JAVA  Frameworks MVC con acoplamiento débil para aplicaciones web: SpringMVC.  Inversión of Control se está incluyendo de forma nativa en lenguajes y librerías de sistema: JDK7 (Direct Injection nativo), Spring IoC, Plexus, etc.  EJB 2.1 se simplifica en EJB 3.0 (conversión de frameworks heavyweight en lightweight)  EJB 3.0 adopta conceptos de frameworks ligeros como Spring Framework.  Uso de Plain Old Java Objects (POJO)  Entornos que además de proporcionar arquitecturas y diseños independientes, flexibles, desacopladas y ágiles, proporcionan mayor ayuda al unit testing.
  117. 117. DECIDIENDO: UNIT TESTING VS INTEGRATION TESTING Las capas de presentación y persistencia siempre representan un problema para su desarrollo usando TDD, que se reduce a decidir entre Unit Tests o Integration Tests. UNIT TESTS *  Cumplen las propiedades FIRST: se pueden usar en TDD.  Los test se deben lanzar continuamente en el equipo de cada desarrollador para poder avanzar en los desarrollos con escala reducida de incrementos que propone TDD.  La misión de un Test Unitario es validar el código bajo prueba de forma aislada.  El aislamiento se consigue utilizando mocks (o sus variantes).  Se puede utilizar test de integración de pequeña escala.  CUIDADO: no convertir el test en un test de las dependencias del Object Under Test.  El test unitario prueba únicamente el código del Object Under Test.  Se asume que las dependencias funcionan correctamente, se codificaron usando TDD.  El aislamiento es clave para no convertir el Unit Testing en una carga adicional.  Probar que de forma aislada el código acepta los datos de entrada, ejecuta el código sin errores y devuelve los datos esperados. * Unit Test: Small Scaled Tests, test unitarios aislados o test de integración de pequeña escala.
  118. 118. DECIDIENDO: UNIT TESTING VS INTEGRATION TESTING INTEGRATION TESTS *  No cumplen las propiedades FIRST: no se pueden usar en TDD.  No se pueden lanzar continuamente avanzando en pequeños incrementos.  No dirigen los desarrollos desde la codificación de forma tan directa.  Se pueden automatizar por adelantado: Test First Development.  Se ejecutarán de forma más espaciada (más lentos).  Cada x horas o minutos, en cada commit, etc.  Tareas configuradas en la herramienta de Integración Continua.  Suelen ser propiedad del grupo de calidad, testing o QA.  NECESARIOS: validan las colaboraciones y detectan los fallos que no se pueden detectar con test unitarios.  Test de Integración en TDD.  Se pueden usar test de integración de pequeña escala para simplificar código.  El desarrollador puede mantener una batería paralela de test de integración. * Integration Test: O también test funcionales, no funcionales, de aceptación de sistema, de interfaz de usuario, etc.
  119. 119. CAPA DE PRESENTACIÓN Y PERSISTENCIA RECOMENDACIONES  Evitar la excesiva redundancia en el código de los tests.  Muchos test unitarios se pueden reutilizar como test de integración con solo cambiar las dependencias en la plataforma de preproducción.  Valorar si es más productivo usar test de integración.  Los test unitarios podrían requerir un uso intensivo de mocks.  Los test de integración podrían ser una repetición de los test unitarios.  Programando con TDD se intenta aligerar código de las capas de presentación y persistencia.  ¿Merece la pena crear test unitarios para capas tan ligeras?  ¿Es más productivo si directamente programamos test de integración?  ¿Existe un grupo de QA programando buenos test de sistema?  ¿Podemos aprovecharlo para evitando la redundancia en capas complicadas?  No existe receta mágica, se debe valorar la situación específica. * Ver notas de la diapositiva.
  120. 120. CAPA DE PRESENTACIÓN Y PERSISTENCIA RECOMENDACIONES  Minimizar la lógica de las capas difíciles de testear.  PERSISTENCIA (código de acceso a datos): Usar Data Access Objects.  Se extrae y se centraliza el código de acceso a base de datos (SRP).  PRESENTACIÓN: Usar arquitecturas MVC, delegando la “C” sobre la capa de negocio.  El controlador delega todo el trabajo sobre la capa de negocio.  El controlador solo contiene código repetitivo de validación de entrada, acceso a la capa de negocio, publicación del modelo y redirección a la vista adecuada.  Plantear cambio de test unitarios por integración (o sistema).  El código de estas capas será muy simple y repetitivo.  Será sencillo localizar fallos en estas capas aunque solo cuenten con test de integración o sistema.
  121. 121. CAPA DE PRESENTACIÓN  Minimizar el código existente en la capa de presentación.  Nunca debe contener lógica de negocio.  La funcionalidad especificada mediante los test de aceptación (ATDD).  Solo debe contener código directamente relacionado con la Interfaz de Usuario.  Dividir responsabilidades: Arquitecturas MVC.  VISTAS  Renderizan el modelo que han construido los controladores.  No suelen admitir Unit Testing: Muchas veces usan lenguajes declarativos.  MODELO  Clases simples que solo contienen propiedades (o los objetos del dominio).  Unit Testing si contienen lógica específica: por ejemplo propiedades generadas en lugar de respaldadas por atributos.  CONTROLADOR  Delegar toda la lógica sobre los objetos de la capa de negocio.  Tareas repetitivas: Procesar la entrada del usuario, exportar un modelo a las vistas, seleccionar la vista y entregarla al usuario.  El desarrollador debe decidir si crear test unitarios o depender de test de más alto nivel.  Muy repetitivo: seguro que se puede reutilizar código de test en todos los controladores  La lógica de presentación en conjunto quedará probada por test de integración, de sistema o de interfaz de usuario (si se dedican recursos de QA).
  122. 122. CAPA DE PRESENTACIÓN Extraer responsabilidades aplicando el principio SRP  Independizar o extraer la lógica compleja de los controladores.  Extracción de datos entregados por el usuario, validación y conversión de los datos, etc.  Responsabilidades con lógica específica de presentación pero que puede ser extraída y reutilizada.  Ej. Validación y conversión de una fecha en forma de texto.  Lógica que debe ser desarrollada con TDD (con sus tests unitarios).  Se puede testear fácilmente si se extrae de los controladores.  Ejercicios La lógica de extracción, verificación y conversión de los datos introducidos en un formulario se ha extraído desde AddContactController en la nueva clase ContactCommandConverter que ahora tiene tests específicos de la lógica.
  123. 123. CAPA DE PRESENTACIÓN APLICACIONES WEB La programación de pruebas unitarias presenta dificultades cuando se depende de un entorno de contenedor no preparado para el Unit Testing (Heavyweight Framework). Dificultades para cumplir con las propiedades FIRST (uso exhaustivo de mocks). Es preferible contar con un framework MVC ligero (Lightweight Framework) que permita aislar fácilmente los controladores, y proporcione utilidades especificas para el testing.
  124. 124. APLICACIONES WEB UNIT TESTING  Parámetros de entrada y salida: uso de fake/stub/mock/spy.  El contenedor es el encargado de crear los parámetros de entrada y salida.  Ej. En un servlet la entrada y salida es el request y response, y el código puede alterar el estado de objetos compartidos como el context y session.  Creación de fakes o librerías de terceros con fakes completos.  Ej. El HttpServletRequest no proporciona un setParameter. Mocks de Sprint Tests Utilities o HttpServlet Unit. IN-CONTAINER UNIT TESTING  Ejecutar los test unitarios directamente en el contenedor de preproducción.  Evita el uso de mocks usando los objetos reales del contenedor.  Los tests unitarios son exactamente iguales, pero sin utilizar mocks.  Ej. Java: CACTUS.  Permite ejecutar Unit Tests de Serlvets, Filters, JSPs, EJBs, etc en cualquier contenedor J2EE.
  125. 125. APLICACIONES WEB INTEGRATION TESTING  Herramientas que permiten la programación test de sistema o integración.  Independientes de la tecnología utilizada en el desarrollo de la aplicación web.  Lanzan peticiones HTTP y proporcionan diferentes herramientas de verificación. EJEMPLOS  HTTP Client Library: cualquier cliente HTTP permite la ejecución de pruebas de integración sobre una aplicación web, una api rest, una api soap, etc.  Ej. curl, httperf, Apache HttpClient, etc.  Programación y simulación manual del entorno del browser y la verificación de respuestas.  HtmlUnit: Los test se deben programar en Java (no la aplicación web).  Emula navegadores específicos sobre la máquina virtual de Java.  Selenium: La programación de los test se puede realizar con: HTML, Java, C#, Perl, PHP, Python, y Ruby. Proporciona un IDE de generación automática de tests.  Ejecuta los test sobre un navegador real en un sistema operativo determinado.  Ambos permiten lanzar las peticiones simulando eventos sobre el DHTML. Ofrecen facilidades de verificación del contenido de las páginas web a cualquier nivel de detalle, estructura de navegación, Test de Aceptación, etc. “¿Qué se debería verificar en los test de integración?” “¿Es conveniente incluso la verificación de un CSS?”
  126. 126. APLICACIONES DESKTOP La interfaz de usuario de una aplicación de escritorio (o una aplicación para móvil) se puede desarrollar usando TDD. Si existe código ejecutable se puede programar un test unitario. Las aplicaciones para móviles se desarrollan sobre un equipo, por tanto se pueden ejecutar los test unitarios en el IDE de desarrollo. UNIT TESTING  Se aplican las ideas comentadas para la capa de presentación.  Utilización de arquitecturas MVC: centralización de la lógica del GUI.  Delegación en la lógica de la capa de negocio.  Extracción de la lógica compleja y específica en forma de responsabilidades. INTEGRATION TESTING  Herramientas para la programación de test de sistema y de integración.  Permiten la manipulación de la Interfaz de Usuario desde el código.  Herramientas específicas, limitadas y complejas.  Ej. UIAutomation, NUnitForms, etc.
  127. 127. CAPA DE PERSISTENCIA Desde un punto de vista purista, los test que ejercitan el código de acceso a base de datos no pueden ser considerados test unitarios, ya que dependen de una entidad externa, la base de datos. ¿Son test de Integración? ¿Test funcionales? ¿Test No Unitarios? No hay receta mágica: la categoría dependerá del contexto.  Aplicar las ideas ya expuestas para la capa de persistencia.  Centralización de la lógica de acceso a datos en Data Accesss Objects (DAO).  Los sistemas de Object Relational Mapping (ORM) alivian el unit testing.  DECIDIR: Integration Testing vs Unit Testing.  EJEMPLO DE TÉCNICAS MÁS COMUNES  Frameworks de testing de base de datos (Ej. DbUnit).  Bases de datos embebidas o en memoria (Standard SQL)  Base de datos de desarrollo o preproducción (rollback al finalizar cada test para inicializar los datos de prueba).
  128. 128. EXPLORATORY TESTING - SPIKE SOLUTIONS A style of software testing that emphasizes the personal freedom and responsibility of the individual tester to continually optimize the quality of his/her work by treating test-related learning, test design, test execution, and test result interpretation as mutually supportive activities that run in parallel throughout the project. — Cem Kaner (*)  Acceptance Test Driven Development (ATDD) propone actividades de Exploratory Testing al finalizar la fase de desarrollo.  Extreme programming propone la creación de Spike Solutions: pequeños tests que permiten la exploración de alternativas.  Reducción del riesgo: Desarrollo de pequeñas alternativas para la acotación de riesgos (test de rendimiento, de carga, etc.).  Diseño basado en el aprendizaje del sistema a producir mediante la exploración de alternativas a través del testing.  Aprendizaje de nuevas tecnologías mediante la experimentación.  No recomendable la aplicación de nuevas tecnologías, frameworks o arquitecturas sin un conocimiento básico de su funcionalidad y las repercusiones sobre el sistema.  Discusión de ideas: Explicar soluciones mediante el código y sus tests. * Cem Kaner: Profesor de Ingeniería del Software en el Florida Institute of Technology, Director del Florida Tech's Center for Software Testing Education & Research.
  129. 129. TEST DRIVEN DEVELOPMENT ANTI-PATRONES Y MALAS PRÁCTICAS
  130. 130. TDD ANTI-PATTERNS Extraído del blog: http://blog.james-carr.org/2006/11/03/tdd-anti-patterns/ — James Carr. THE LIAR •EL MENTIROSO. Todos los asserts se cumplen pero el test no está probando la funcionalidad que realmente se quería probar. EXCESSIVE SETUP •EXCESIVA CONFIGURACIÓN . Demasiado código en los Fixtures o configuración, que impide ver lo que se está probando o la finalidad del test. THE GIANT •EL GIGANTE. El test es correcto pero contiene demasiado código, probando muchos casos de uso. O una clase de test con una cantidad enorme de tests. Indica necesidad de aplicar SRP. THE MOCKERY •EL EMULADOR. Se abusa de los mocks, stubs o fakes. El sistema no acaba siendo testeado, se están validando los datos devueltos por los mocks. Demasiada verificación por interacción. El test es un reflejo del código funcional, que tan solo prueba cómo se usan las dependencias.
  131. 131. TDD ANTI-PATTERNS THE INSPECTOR • EL INSPECTOR. Test que viola la encapsulación (partes privadas) o desvela demasiados detalles de implementación para conseguir 100% de cobertura. Cualquier refactorización posterior romperá el test. GENEROUS LEFTOVERS • SOBRAS ABUNDANTES. Tests que violan la propiedad de independencia. Los datos de un test se mantienen y no se limpian, aprovechándose o interfiriendo en otros tests. THE LOCAL HERO • EL HÉROE LOCAL. Los tests solo funcionan en una plataforma específica o con una configuración específica. No sirven para TDD, los tests se deben ejecutar en el equipo de cualquier desarrollador. THE NITPICKER • EL QUISQUILLOSO. El test valida la salida completa o demasiados detalles no significativos. Cada pequeño cambio requiere el mantenimiento del test. (Ej. Comparar el title o el css de las páginas html de una aplicación web).
  132. 132. TDD ANTI-PATTERNS THE SECRET CATCHER •CAZADOR SECRETO. Parece que no realiza verificaciones por ausencia de asserts, pero en realidad verifica que no se lanza alguna excepción. Conviene aclarar la finalidad capturando la excepción y finalizar con un fail. THE DODGER •EL DESERTOR. Test que prueba muchos efectos colaterales, pero no termina de probar la autentica funcionalidad el Object Under Test. Ej. Verificar que no se alteran datos, o no se ejecutan ciertos métodos de las dependencias, etc. THE LOUDMOUTH •EL ALBOROTADOR. Llena la consola de demasiados datos de diagnostico, trazas, mensajes, etc. incluso cuando se pasan los tests. Algunas veces se crean estas trazas durante la codificación de los test, y al final se dejan aunque no sean necesarios (enturbiando los resultados). THE GREEDY CATCHER •CAZADOR GLOTÓN. Captura e ignora las excepciones (aunque escriba trazas), dejando pasar los tests. Problemática muy habitual en aplicaciones Java (incluso en código funcional).
  133. 133. TDD ANTI-PATTERNS THE SEQUENCER • EL SECUENCIADOR. Fija el orden de los resultados. Los asserts esperan que los resultados se reciban siempre en el mismo orden (métodos que devuelven listas). HIDDEN DEPENDENCY • DEPENDENCIA OCULTA. Test que presupone la existencia de ciertos datos de test en la plataforma de ejecución. Otros desarrolladores deben descubrir que datos se supone que deberían existir. THE ENUMERATOR • EL ENUMERADOR. Los nombres de los métodos de test son sólo una enumeración como test1, test2, test3, en lugar de utilizar nombres significativos que indiquen la intención de cada uno de los tests. THE STRANGER • EL EXTRAÑO. Test que está realizando pruebas sobre un objeto que no pertenece al Object Under Test de la clase de test. Una clase de test se debería encargar de los test de una sola clase del código funcional. También El Pariente Lejano (Distant Relative).
  134. 134. TDD ANTI-PATTERNS THE OPERATING SYSTEM EVANGELIST •EVANGELISTA DEL SISTEMA OPERATIVO. Los test que dependen de caracteristicas del sistema operativo. Ej. Suposiciones de los caracteres de nueva línea, comandos de la shell, etc. SUCCESS AGAINST ALL ODDS •A PESAR DE TODO FUNCIONA. Se escribe el código funcional antes de hacer fallar al test aunque este se escribiera antes, o se escriben test sobre código que ya existe. Un efecto lateral es que el test puede pasar aunque el código tenga fallos. THE FREE RIDE •DOS POR UNO. En lugar de escribir un nuevo test para probar una nueva funcionalidad, se añade un nuevo assert en un test ya existente. THE ONE •EL ÚNICO. Combinación de varios patrones (Free ride and Giant). Un solo método de test que prueba todos los casos de uso del objeto en secuencia. Contiene múltiples fixtures y asserts.
  135. 135. TDD ANTI-PATTERNS THE PEEPING TOM • EL MIRÓN. Debido a recursos compartidos un test puede ver los resultados de otro test y puede hacer que el test falle incluso cuando el sistema testeado es válido. Suele producirse con el uso de variables de clase (miembros estáticos). También conocido como The Uninvited Guests. THE SLOW POKE • EL TORTUGA. Un test increíblemente lento. No apto para el desarrollo con TDD, si no para tests ejecutados en la herramienta de integración continua.
  136. 136. MALAS PRACTICAS  IGNORAR LAS PROPIEDADES F.I.R.S.T.  Propiedades ideadas para la aplicación de TDD como una metodología de desarrollo, iterativa, incremental, a pequeña escala y dirigida por unit tests.  No están ideadas para su utilización en metodologías de testing.  NO DEJAR CLARO CUAL ES EL OBJECT UNDER TEST  Test en los que se confunde el Object Under Test entre el resto de Test Objects, mocks, etc.  El Object Under Test debería ser lo más sencillo de identificar.  NO REFACTORIZAR en cada iteración  No se estaría usando la metodología TDD.  La refactorización no se aplaza (los momentos de descanso no existen).  TEST DIFÍCILES DE ENTENDER  Deben funcionar como documentación o ejemplos de nuestra API.  Nombres de tests y código auto-explicativo (fijar estándares).
  137. 137. MALAS PRACTICAS  VARIOS CASOS DE PRUEBA EN UN SOLO TEST  Se debe separar en diferentes métodos de test, cada test solo un assert.  Excepto cuando son asserts que pertenecen a una misma funcionalidad.  MEZCLAR TEST DE INTEGRACIÓN CON TEST UNITARIOS  Se deben mantener en carpetas o proyectos independientes aunque ambos se puedan escribir usando librerías xUnit.  CONVERTIR TEST UNITARIOS EN TEST DE DEPENDENCIAS  El test unitario debe probar única y exclusivamente el Object Under Test.  Aunque se usen dependencias reales, se asume que tienen sus tests.  ABUSAR DE LA VERIFICACIÓN POR INTERACCIÓN  Problemática vista en el tema de mocks y antipatrones.  Si los tests tan solo contienen verify sobre lo mocks, revisar los tests.
  138. 138. ACCEPTANCE TEST DRIVEN DEVELOPMENT ACCEPTANCE TEST DRIVEN DEVELOPMENT EXPLAINED
  139. 139. ACCEPTANCE TEST DRIVEN DEVELOPMENT REFERENCIAS ATDD es una metodología relativamente reciente comparada con el resto de metodologías Agile, aun no hay mucha literatura disponible.  http://www.dosideas.com/wiki/Acceptance_Test_Driven_Development  Pequeña introducción y enlaces a herramientas de automatización.  http://www.methodsandtools.com/archive/archive.php?id=72  Acceptance TDD Explained (Artículo de diez páginas)  http://www.methodsandtools.com/archive/archive.php?id=23  Using Customer Tests to Drive Development  Practical TDD and Acceptance TDD for Java Developers, 2007, Lasse Koskela, Manning Publications  http://www.manning.com/koskela/  Acceptance Test Engineering Guide, 2009, Grigori Melnik, Gerard Meszaros, Jon Bach, Microsoft
  140. 140. ACCEPTANCE TEST DRIVEN DEVELOPMENT “Se acerca más a una metodología de especificación de requisitos que una metodología de testing” NOMENCLATURA  Acceptance Test Driven Development (ATDD)  Story Test Driven Development (STDD)  Customer Test Driven Development (CTDD)
  141. 141. ACCEPTANCE TEST DRIVEN DEVELOPMENT OBJETIVOS  Permitir al cliente obtener un feedback del avance del desarrollo mediante las pruebas de aceptación.  Pruebas de cliente: el cliente puede ser reacio a validar unos tests que comprometen la aceptación del producto.  Obtener una especificación de requisitos mas concreta y menos ambigua mediante el uso de ejemplos.  Los Users Stories se describen mediante ejemplos concretos de uso.  Los User Stories ya no se describen mediante la redacción de descripciones.  Ayudar al cliente a entender lo que realmente quiere  Haciéndolo consciente de la casuística de todos los User Stories  Ayudar a los desarrolladores a entender lo que quiere el cliente.  Concretando el comportamiento de cada User Story y evitando la libre interpretación de la casuística no contemplada en la especificación.  ESPECIFICACIÓN DE REQUISITOS EJECUTABLE
  142. 142. ACCEPTANCE TESTS VS UNIT TESTS ACCEPTANCE TESTS • Escritos por los “clientes”. • Qué hace el sistema. • Tests de Features (Backlog). • Vocabulario del negocio. • Sin detalles implementación. UNIT TESTS • Escritos por desarrolladores. • Cómo se hace el sistema. • Tests del código. • Vocabulario de la solución. • Detalles de implementación.
  143. 143. ACCEPTANCE TESTS PROPORCIONAN  CRITERIO DE FINALIZACIÓN  Proporcionado por el “cliente” a los desarrolladores.  Especificando cada User Story mediante ejemplos.  FOMENTA LA COMUNICACIÓN  Fuerza a clientes, testers y desarrolladores a trabajar juntos.  RAPID FEEDBACK  Herramienta para el Project Management.  Datos reales para medir el avance del desarrollo.  Total de test de aceptación, total fallidos y total cumplidos.

×