Seminario de Test Development Driven

120
Introducción a TDD

description

Seminario de introducción a Test Driven Development organizado por Paradigma Tecnológico y javaHispano. El seminario fue impartido por Carlo Scarioni el 23 de Abril 2010 Mas info: http://www.paradigmatecnologico.com/historico/seminario-de-test-development-driven/

Transcript of Seminario de Test Development Driven

Page 1: Seminario de Test Development Driven

Introducción a TDD

Page 2: Seminario de Test Development Driven

Enfoque de la Charla

Presentar un ejemplo de principio a fin de una funcionalidad de un proyecto. Sin profundizar en las herramientas utilizadas. El objetivo es clarificar el proceso de TDD.

Page 3: Seminario de Test Development Driven

Objetivos de la charla-- Introducir TDD como una alternativa viable al desarrollo tradicional.-- Crear cierta inquietud por profundizar mas en el tema.-- Exponer las ventajas que TDD tiene para el desarrollador.-- Explicar paso a paso como afrontar una funcionalidad con esta práctica.

Page 4: Seminario de Test Development Driven

¿Que es TDD?Practica de desarrollo de software

Test First + Refactor

Page 5: Seminario de Test Development Driven

¿Que ventajas trae TDD al desarrollador?• Confianza en el funcionamiento.• Foco en el desarrollo.• Código mas limpio. (Buenas practicas de

desarrollo y patrones).• Menos Bugs y mas localizados.• Documentación del código con los Tests.• Menos reinicio del servidor para probar.

Page 6: Seminario de Test Development Driven

Realizar el menor diseño posible antes de empezar.

Solo lo necesario (Generalmente la

Infraestructura de la aplicación). Los test guiarán el

diseño.

El ciclo de TDD

Page 7: Seminario de Test Development Driven

El ciclo de TDD

RefactoRefactorr

Escribir un Escribir un test Unitario test Unitario que falleque falle

Hacer que Hacer que el test el test pasepase

Escribir un Test

Funcional Que falle

Page 8: Seminario de Test Development Driven

Conocido como el ciclo ROJO->VERDE-

>REFACTOR

Page 9: Seminario de Test Development Driven

¿Cuanto tiempo pueden estar los tests en rojo?

- - Test Unitarios deben pasar cuanto antes.- - Test funcionales tardarán mas en pasar. Y

estarán en un ciclo distinto del BUILD.- - Nunca se subirán tests que fallan al repositorio

de código fuente.- - Solo se desarrolla funcionalidad cuando exista

un Test fallido que lo requiera.

Page 10: Seminario de Test Development Driven

¿Cuanto del código probar?

- - Probar TODO lo que tenga sentido probar.

- - No probar lo trivial.- - Spikes paraThird Party.- - Cobertura.

Page 11: Seminario de Test Development Driven

¿Como comenzar?.

- - Escoger la funcionalidad (feature) mas pequeña posible que la aplicación deba cumplir.

- - Luego ir escogiendo funcionalidades de nuestro Product Backlog.

Page 12: Seminario de Test Development Driven

Nuestro ejemplo.

Situación Inicial Situación

Final

Page 13: Seminario de Test Development Driven

Iteración 0

Page 14: Seminario de Test Development Driven

¿Que es un test de aceptación o test funcional?

Nos ayuda a definir exactamente la funcionalidad que queremos hacer. Un test define QUE queremos hacer, si importar realmente el COMO.

Page 15: Seminario de Test Development Driven

No debe conocer los objetos internos del sistema.

Debe reaccionar ante eventos que se produzcan en la capa "visible" (GUI, LOG, etc). Usualmente haciendo un poll para ver si hay cambios.

Test Funcional

Page 16: Seminario de Test Development Driven

Prueba la interacción entre distintos componentes del sistema, o entre el sistema y componentes externos. Por ejemplo con la Base de Datos, Con el sistema de ficheros, etc.

Test Integración

Page 17: Seminario de Test Development Driven

Definimos el Test Funcional.Selenium

Page 18: Seminario de Test Development Driven

Definimos el Test Funcional.

Selenium. Permite ejecutar Tests Directamente sobre la interfaz de usuario HTML, sobre un navegador en particular. Para esto la aplicación debe estar iniciada. El proceso de iniciar la apliciación debe ser automático, previo a la ejecución de los test, y terminado luego.

Page 19: Seminario de Test Development Driven
Page 20: Seminario de Test Development Driven

Definimos el Test Funcional.

Este Test podrá estar sin pasar por mucho tiempo. Nos sirve como guía de la funcionalidad final que queremos conseguir.

Page 21: Seminario de Test Development Driven

Prueba comportamientos en aislamiento TOTAL respecto al resto del sistema. Prueba una y solo una caracteristica sin que los demas elementos del sistema afecten su ejecución.

Test Unitario

Page 22: Seminario de Test Development Driven

El primer Test Unitario. La primera abstracción que se nos ocurre es una Cuenta. En la que

podemos depositar y sacar dinero. Hacemos el test para depositar dinero

Page 23: Seminario de Test Development Driven

El primer Test Unitario.

Hacemos el test compilar. Para eso creamos la clase Account y los métodos necesarios.

Page 24: Seminario de Test Development Driven

El primer test unitario.El Test Compila.

Page 25: Seminario de Test Development Driven

El primer Test UnitarioBarra roja

Page 26: Seminario de Test Development Driven

El primer Test unitario. Implementación Falsa VS Implementación obvia. Pasamos el Test con implementación falsa.

Page 27: Seminario de Test Development Driven

Realizamos otro test que nos obligue a sustituir la implementación falsa. Esto es Triangulación en TDD

El primer Test unitario.

Page 28: Seminario de Test Development Driven

Cambiamos la implementación

El primer Test unitario.

Page 29: Seminario de Test Development Driven

La barra verde nuevamente

El primer Test unitario.

Page 30: Seminario de Test Development Driven

Cuando se sepa claramente la implementación obvia, aplicarla.

No es necesario siempre dar los pasos mas pequeños posibles.

Si la implementación obvia resulta no ser tan obvia, y al implementarla los test fallan. Hacer los pasos mas pequeños posibles.

El primer Test unitario.

Page 31: Seminario de Test Development Driven

Es importante hacer tests para situaciones que esperamos

produzcan un error.

Probando los fallos.

Page 32: Seminario de Test Development Driven

Probando los fallos.

Hemos creado por TDD lo siguiente en AccountTest y Account para el retiro de dinero.

Sin embargo como requerimiento tenemos que no podemos dejar una cuenta a un numero negativo. Por tanto hay que comprobar que esto no se pueda dar.

Page 33: Seminario de Test Development Driven

Probando los fallos.

Con esto decimos que esperamos que al producirse esta situación (Rerirar mas de lo que tenemos), se eleve la excepción WithdrawlException (Que aun no existe).

Page 34: Seminario de Test Development Driven

Probando los fallos.

Creamos la excepción. Nuestro Test Compila

Page 35: Seminario de Test Development Driven

Probando los fallos.Ejecutamos el Test. Barra Roja

Page 36: Seminario de Test Development Driven

Añadimos la implementación obvia. Ejecutamos. Barra Verde.

Probando los fallos.

Page 37: Seminario de Test Development Driven

Probando los fallos.

Refactorizamos con un sencillo Extract Method. Ejecutamos. Barra Verde.

Page 38: Seminario de Test Development Driven

Continuando con Account. Soporte de Divisas

La siguiente caracterisitac que deben soportar nuestras cuentas, es que pueden estar en EUR, USD o GBP.Comenzamos con el TEST.

Page 39: Seminario de Test Development Driven

Continuando con Account. Soporte de Divisas.

Cuando uno crea un Test, la idea es imaginar el API mas correcto de lo que estamos creando. En este caso nos damos cuenta que una Cuenta DEBE tener una divisa. Por lo que la ponemos en le constructor.

Page 40: Seminario de Test Development Driven

Hacemos los cambios necesarios a Account para que compile y damos la solución obvia. Nuestros Tests anteriores ya no compilan (requerían constructor vacio). Cambiamos la construcción de las Accounts para el nuevo constructor. Ejecutamos los Tests. Barra verde.

Continuando con Account. Soporte de Divisas.

Page 41: Seminario de Test Development Driven
Page 42: Seminario de Test Development Driven

Es muy importante ejecutar TODA la suite de Tests cuando se hacen cambios y refactorizaciones. Ya que aunque en el caso anterior se detectó en compilación el fallo (La falta de constructor). Muchas veces un cambió hará que cosas que asumiamos como correctas ya no lo sean. Y habrá que adaptar los Tests y el código a la nueva solución.

Continuando con Account. Soporte de Divisas.

Page 43: Seminario de Test Development Driven

RefactorizaciónUno de los mas importantes "Code Smells", es el hecho de utilizar Strings para representar cosas que son mas que texto y tienen significado propio. En nuestro caso, el String que se pasa al constructor.Creamos un ENUM para representar nuestras divisas. Adaptamos los Tests. Barra Verde

Continuando con Account. Soporte de Divisas.

Page 44: Seminario de Test Development Driven

Continuando con Account. Soporte de Divisas.

Page 45: Seminario de Test Development Driven

Añadimos también, por comodidad un constructor que aparte de la divisa, se le pueda enviar también el balance inicial.

Continuando con Account. Soporte de Divisas.

Page 46: Seminario de Test Development Driven

Probando el servicio de transferencia.Dejando Account a un lado, considerandolo terminado, nos centramos en el servicio de transferencia.Para nosotros una transferencia será sencillamente, retirar de una cuenta, y depositar en otra.

Page 47: Seminario de Test Development Driven

El primer test que se nos ocurre es hacer una transferencia entre 2 cuentas con la misma divisa.

Probando el servicio de transferencia.

Page 48: Seminario de Test Development Driven

El código anterior es lo primero que se nos ocurre realizar. Sin embargo, viendo la interfaz del método transfer, los parámetros se prestan a confusión. ¿De que cuenta a que cuenta es la transferencia?

Probando el servicio de transferencia.

Page 49: Seminario de Test Development Driven

Decidimos que un mejor diseño es encapsular la transferencia es su propio objeto y pasar este objeto al metodo transfer.

Probando el servicio de transferencia.

Page 50: Seminario de Test Development Driven

La TransferOperation es mucho mas especifica. Creamos las clases que nos faltan

Probando el servicio de transferencia.

Page 51: Seminario de Test Development Driven

Ejecutamos nuestro Test. Barra Roja!!.

Probando el servicio de transferencia.

Page 52: Seminario de Test Development Driven

Haciendo una inspección de que puede estar fallando, nos damos cuenta que el error no esta en el servicio, ni en el Test.. sino en Account (Podriamos hacer un mock temporal de la clase account y ver que se llaman al deposit y withdrawal correctamente. Pero el servicio es muy simple y resulta obvio). Clase que habiamos dada por terminada. Vemos que el problema está en el metodo deposit. Por no haber triangulado lo suficiente en las pruebas.

Probando el servicio de transferencia.

Page 53: Seminario de Test Development Driven

Prueba de Regresión. Se reporta un Error (Bug) por una funcionalidad dada por cerrada y que por falta de input no se hizo el test adecuado. Normalmente la falta de input es por requerimientos no entendidos completamente. En nuestro caso es que no hicimos todas las pruebas que debimos.

Probando el servicio de transferencia.

Page 54: Seminario de Test Development Driven

Vamos a AcountTest. y agregamos un Test para el error que hemos obtenido. Barra Roja

Probando el servicio de transferencia.

Page 55: Seminario de Test Development Driven

Vamos a la clase Account e implementamos de forma correcta (que al menos pase este test que es lo que entendemos por correcto) el metodo deposit.

Probando el servicio de transferencia.

Page 56: Seminario de Test Development Driven

Ejecutamos nuestro Test AccountTest. Barra Roja otra vez.

Probando el servicio de transferencia.

Page 57: Seminario de Test Development Driven

Vemos que ahora el balance es null en deposit cuando aun no se ha depositado nada. La ejecución de los Tests, siempre va a capturar comportamientos no deseados como este, si se hacen los tests correctos.Corregimos Account y ejecutamos el Test. Barra Verde. Al fin.

Probando el servicio de transferencia.

Page 58: Seminario de Test Development Driven

Regresamos al punto donde realmente estabamos. En TransferTest. Lo ejecutamos. Barra Verde.

Probando el servicio de transferencia.

Page 59: Seminario de Test Development Driven

Refactoring.La forma de construir la TransferOperation no es del todo agradable. sustituimos los setters actuales por un semi-builder mas intutitivo y comodo, definido con un DSL propio. Cambiamos el Test a como realmente queremos que luzca la llamada.

Probando el servicio de transferencia.

Page 60: Seminario de Test Development Driven

Cambiamos el codigo de TransferOperation, Ejecutamos el Test, Barra Verde.

Page 61: Seminario de Test Development Driven

Transferencias entre 2 monedas distintas.Diseñamos nuestro Test. Primera aproximación. Sin factor de conversión. Barra Verde

Probando el servicio de transferencia.

Page 62: Seminario de Test Development Driven

Transferencias entre 2 monedas distintas.

Con factor de conversión. Primera aproximación

Probando el servicio de transferencia.

Page 63: Seminario de Test Development Driven

Transferencias entre 2 monedas distintas.

Con factor de conversión. Primera aproximacion. Hacemos compilar el codigo. Ejecutamos. Barra roja

Probando el servicio de transferencia.

Page 64: Seminario de Test Development Driven

Transferencias entre 2 monedas distintas.

Con factor de conversión. Primera aproximación. Implementamos.

Probando el servicio de transferencia.

Page 65: Seminario de Test Development Driven

Transferencias entre 2 monedas distintas.Con factor de conversión. Primera aproximación. Ejecutamos los Tests. Barra Roja

Probando el servicio de transferencia.

Page 66: Seminario de Test Development Driven

Transferencias entre 2 monedas distintas. Se arregla el bug que se acaba de introducir. Ejcutamos los Test. Barra Verde. (Ojo tambien habría que verificar, con test que los valores están metidos en el mapa de conversionRates)

Probando el servicio de transferencia.

Page 67: Seminario de Test Development Driven

Transferencias entre 2 monedas distintas. Refactorización SRP. Necesitamos otro servicio, Currency Service que se ocupe de la conversión. TransferService solo sabe de transferencias.

Probando el servicio de transferencia.

Page 68: Seminario de Test Development Driven

Creamos la implementación extrayendo una clase nueva de TransferServiceImpl.

Page 69: Seminario de Test Development Driven

Incluimos la dependencia en Transfer Service para que utilice la nueva dependencia.

Page 70: Seminario de Test Development Driven

Ejecutamos CurrencyTest. Barra Verde. Sin emabrgo luego de una amplia refactorización como la hecha es necesario ejecutar toda la suite de tests para verificar que todo está correcto.

Page 71: Seminario de Test Development Driven

Transfer Test se ha roto con la refactorización, ya que se ha incluido una nueva dependencia que no habiamos considerado cuando hicimos el servicio. Debemos adaptar nuestro Test a esta nueva casuistica.

Page 72: Seminario de Test Development Driven

En el caso anterior en realidad se debía hacer el test antes de añadir la dependencia a la clase implementación.

Siempre se debe atender primero al Test según los requerimientos y el comportamiento que se quiere lograr

Page 73: Seminario de Test Development Driven

Mock Objects

Introduciremos un Mock para la dependencia.

¿Qué es un Mock Object?

Page 74: Seminario de Test Development Driven

Mock Object. Mock hecho a mano

Page 75: Seminario de Test Development Driven

JMOCK

Nos permite con un lenguaje especifico de dominio definir Dobles de objetos para nuestros Test, y establecer las expectativas sobre estos objetos.

Page 76: Seminario de Test Development Driven

Mock con Jmock. Y expectativas. Ejecutamos todos los tests. Barra verde

Page 77: Seminario de Test Development Driven

El uso de mocks

El uso de mocksSe puede establecer su necesidad en cualquiera de los dos tiempos de diseño. Escribir el Test, o la refactorización.

Page 78: Seminario de Test Development Driven

Refactorizando CurrencyService. Ese mapa de mapas es muy lioso. Se nos ocurre mover la responsabilidad de saber el rate de cambio a la propia divisa.

Page 79: Seminario de Test Development Driven

Para hacer esto decidimos también conversión establecer Rates a una divisa única y utilizar esta para los cambios. Usamos el Dólar.

Page 80: Seminario de Test Development Driven

Escribimos los Tests (En realidad se escribieron 1 a 1 estos tests). Y Ejecutamos, Barra Roja.

Page 81: Seminario de Test Development Driven

Implementamos

Page 82: Seminario de Test Development Driven

Ejecutamos los tests. Green Bar

Refactorizamos CurrencyService

Page 83: Seminario de Test Development Driven

Persistiendo los cambios

Persistiendo los cambios. Servicio de persistencia de cuentas. Mock del Dao en Las transferencias. TDD. AccountService debe permitir persistir cuentas. No compila. Añadimos una nueva propiedad a la clase

Account. El identificador. AccountNumber. Aqui incluimos el mock antes de implementar nada. Sabemos que por buena practica el Servicio no irá directo contra Repositorio.

Page 84: Seminario de Test Development Driven

Persistiendo los cambios

- Diseñamos el Test con las dependencias en Mente.

- Nos damos cuenta de la necesidad de un identificador de cuenta. Y lo incluimos en nuestro Test.

- Siempre pensar en como queremos invocar a nuestras APIs

Page 85: Seminario de Test Development Driven

Lo hacemos compilar. Implementamos en account service. Ejecutamos el test. Barra Verde.

Page 86: Seminario de Test Development Driven

Test Driving el DAO. Test de Integración.Ahora haremos la implementación del DAO guiado por tests. Exactamente igual que como hasta ahora. Solo que ahora interactuamos con elemento externo. La Base de Datos, o sistema de ficheros. Pero empecemos por el test.

Page 87: Seminario de Test Development Driven

Utilizaremos el poderoso Spring Test Context para las pruebas de integración. (Las siguientes pruebas se hicieron 1 a 1).

Page 88: Seminario de Test Development Driven
Page 89: Seminario de Test Development Driven

Implementamos (En realidad lo anterior se hizo test a test. Pero para recortar lo ponemos todo de hecho con la primera ejecución nos dimos cuenta que Account debía ser serializable y debiamos implementar el equals y el hashcode para comparar Accounts) .

Page 90: Seminario de Test Development Driven
Page 91: Seminario de Test Development Driven

En algunos casos, los tests de integración son bastante mas lentos que los tests unitarios como vemos.Ejecutamos la suite entera de tests

Page 92: Seminario de Test Development Driven

Persistiendo las transferencias

El servicio de transferencias ahora mismo no persiste los datos. Debemos añadir esto a nuestra aplicación. Lo añadimos en el Test, lo hacemos compilar, y lo implementamos en el servicio. Con el mismo procedimiento paso a paso hecho hasta ahora. Al final obtenemos el siguiente resultado. Obteniendo la barra verde.

Page 93: Seminario de Test Development Driven
Page 94: Seminario de Test Development Driven
Page 95: Seminario de Test Development Driven
Page 96: Seminario de Test Development Driven

Ejecutamos. Barra Verde

Page 97: Seminario de Test Development Driven

TDD el controlador.

Se basa en los mismos procedimientos que el probar las otras capas. Centrarnos en la funcionalidad requerida, y simular los demas componentes. Las dependencias. Sabemos que trabajaremos con Controllers de Spring.Lo primero que haremos será la funcionalidad para presentar el formulario.

Page 98: Seminario de Test Development Driven
Page 99: Seminario de Test Development Driven

TDD El controlador.

Lo hacemos compilar.Ejecutamos. Barra Roja

Page 100: Seminario de Test Development Driven
Page 101: Seminario de Test Development Driven

TDD el controlador

Implementamos. Ejecutamos el Test. Barra Verde. En principio no hay refactorización que hacer.

Page 102: Seminario de Test Development Driven

TDD el controlador. El submit del formularioMocking HttpServletRequest.

Page 103: Seminario de Test Development Driven

TDD el controlador. Implementamos. Barra Verde.

Page 104: Seminario de Test Development Driven

Ejecutamos la Suite Entera de Tests. Barra Verde.

Page 105: Seminario de Test Development Driven

El test funcional. Creamos las JSPs requeridas segun nuestros Test, nuestro controller y nuestro conocimiento de Spring MVC.

Transfer.jsp

Page 106: Seminario de Test Development Driven

transferResult.jsp

Page 107: Seminario de Test Development Driven

Todo dentro del Test

El Test funcional idealmente es capaz de iniciar todo su entorno. En nuestro caso iniciamos el Tomcat.

Page 108: Seminario de Test Development Driven

Utilizamos Cargo para controlar el servidor.

Page 109: Seminario de Test Development Driven

Preparando para el Test Funcional

Page 110: Seminario de Test Development Driven

Las 2 fases del BUILD

Page 111: Seminario de Test Development Driven

Ejecutando el Test Funcional. Incluimos Un setup Completo en el test que inicia el servidor tomcat.

Page 112: Seminario de Test Development Driven

Ejecutamos la Suite Entera

Incluyendo Funcionales

Page 113: Seminario de Test Development Driven

Las 2 Fases del BUILD

En un entorno de integración continua.

Mvn test: Ejecuta los Tests unitarios, se ejecutara en cada momento

Mvn integration-test: Ejecuta los Tests funcionales. 2 veces al dia.

Page 114: Seminario de Test Development Driven

Revisando el Code Coverage

Cobertura. Mvn site

Mas que medir la cobertura por porcentaje. Estar conscientes de que hemos probado lo necesario.

Page 115: Seminario de Test Development Driven

Reporte del Ejemplo

PACKAGE CLASES LINE COVERAGEALL PACKAGES 16 75%ORG.PT.TDD 1 92%ORG.PT.TDD.DAO 2 75%ORG.PT.TDD.DOMAIN 3 69%ORG.PT.TDD.EXCEPTION

3 50%

ORG.PT.TDD.FORM 1 100%ORG.PT.TDD.SERVICE 6 90%

Cobertura

136/179

15/20

26/28

64/92

6 12

18/20

7/7

Page 116: Seminario de Test Development Driven

Cobertura

Page 117: Seminario de Test Development Driven

Cobertura

Page 118: Seminario de Test Development Driven

Conclusiones

-- TDD nos ayudan a mantener el foco de lo que queremos desarrollar.-- TDD nos sirve como red de seguridad para atrapar Bugs lo antes posible.-- TDD nos da seguridad de que lo que desarrollamos funciona.

Page 119: Seminario de Test Development Driven

Bibliografía

Page 120: Seminario de Test Development Driven

Preguntas