martes, 14 de septiembre de 2010

1.3 Construir bien: TDD

Desarrollo basado en pruebas es una técnica de diseño y desarrollo que nos ayuda a construir el sistema de forma incremental. Una prueba es una manera de tomar un pequeño paso.

En esta sección vamos a aprender lo que hace TDD y vamos a más detalles sobre por qué funciona y qué tipo de beneficios obtenemos con el uso de esta técnica. Todo comienza con el ciclo de TDD, que es el latido del corazón de nuestro trabajo. Después de explorar el ciclo TDD, hablaremos sobre el significado de tener un software de trabajo todo el tiempo, comenzando desde el primer día. Una parte esencial de la construcción del sistema de forma incremental es diseñar para lo presente. Lo siguiente es , hablar sobre el ciclo de prueba TDD-código refactorización.

1.3.1 Ensayos de refactorizar código: el latido del corazón

Como hemos aprendido en el primer párrafo de este capítulo, el desarrollo basado en pruebas, o TDD, es una técnica de programación baado en una regla muy simple:

Sólo una vez escribir código para establecer una prueba de errores.

En otras palabras, escribir la primera prueba, y sólo a continuación , escribir el código definitivo que funcione.
Esta norma es motivo de controversia para muchos de nosotros que hemos sido instruído para producir diseño de fondo y luego implementar el diseño, y finalmente probar nuestro software. Para encontrar todos los errores que hemos inyectado en la ejecución. TDD lo convierte a este ciclo como se ilustra en la figura 1.3

Prueba primero el código y después implementa el diseño. ¿El pensamiento de "diseñar después" se siente incómodo? Eso es natural. No es el mismo tipo de diseño que estamos acostumbrados en el proceso tradicional de diseño de código de prueba. Lo llamamos refactorización para mejorar el diseño como se muestra en la figura 1.4: código test-refactoring.





En su simplicidad engañoza, este pequeño ciclo, abarca un significativo poder para mejorar la calidad general de nuestro proceso de software y posteriormente, la de todo el equipo, poryecto y organización.

Rojo, verde y refactorizar

Rojo, verde y refactorizar es una alternativa para el ciclo TDD de escribir pruebas a ejecutar, lo que es bonito. ¿Qué pasa con los colores, te pregunatrás? Cuando comenzamos por el ciclo TDD por escrito y una prueba falla. Se produce un error porque nuestro sistema no funciona en este momento, no tiene toda la funcionalidad que se desea que tenga. En algunos entornos de desarrollo, se produce un error al mostrar una barra de color rojo- por lo tanto el rojo en la regla nemotécnica. En el segundo paso, es la implementación de la funcionalidad que falta para que todas las pruebas corran, tanto la que acaba de agregar y todos los que teníamos ya.
En este momento, la barra roja cambia a verde, lo que nos lleva a verde la regla nemotécnica. La última parte del ciclo, es la refactorización. A medida que mejoremos el diseño del código sin alterar su comportamiento externo, todas las pruebas deben pasar y por tanto, debemos seguir siendo verde. Rojo, verde, Rojo, verde, refactorizar: Muy atractivo no?.

Vamos a echar un vistazo más de cerca este ciclo en el capítulo 2, pero vamos a hecer una descripción rápida de lo que hacemos en cada uno de estos pasos y por qué las hacemos. A continuación, vamos a explotar, aún más la lógica y la dinámica detrás de la técnica.

Primero escribimos una prueba

Cuando escribimos un caso de prueba en la primera etapa del ciclo de TDD, en realidad lo que estamos haciendo, no es más que , escribir un plan de prueba. Estamos construyendo las decisiones del diseño. Estamos diseñando las API - la interfaz para el acceso a la funcionalidad que se está probando. Al escribir el plan de pruebas antes de escribir el código, estamos comprobando, esforzándonos en pensar mucho cómo se desea utilizar el código. Es un poco como armar un rompecabezas. Como se ilustra en la figura 1.5, es difícil conseguir la pieza que se necesita, si no se conoce la pieza que se debe conectar.


Esto no es algo para tomarse a la ligera. Usted puede haber oído a especialistas de interfáz de usuarios donde consideran lo importante que es diseñar interfaces de usuarios. ¿Por qué,? Si las condiciones de las partes internas de un software son diferentes? ¿No somos los programadores, los dueños de nuestro código al igual que los usuarios finales son dueños de nuestro software ?

Esta forma de pensar acerca del código a veces puede hacer una gran diferencia. Una de las leccione fundamentales en el diseño de una interfaz es que sólo hay que evaluar un diseño eficaz y objetivo cuando tratamos de utilizarlo. Para escribir la primera prueba , estamos asegurando que no se perderá ningún detalle.

La granularidad al escribir las pruebas es algo que debemos poner atención.Si nos esforzamos en escribir código de prueba, basta con tener una prueba en su defecto, en lugar de escribir una prueba épica de tamaño de una pieza funcional y que va a tomar una hora de implementar.

Nota: Dependiendo del dominio, las herramientas y tecnologías en cuestión, la escritura de la prueba puede ser generada en unos segundos o puede que tome un par de minutos. La puesta en práctica de una prueba general, debe estar dentro del intervalo de tiempo. El uso de de tecnologías complejas empujan a nuestra granularidad y ritmo hacia un largo final.

No es fácil crear API sencillas de utilizar. Es por eso que necesitamos toda la ayuda que podamos. Como resultado, la conducción de nuestro diseño con las pruebas es muy eficaz y produce código modular, comprobables. Por definición, el código que escribimos es comprobable de lo contrario no existiría.

El diseño de software no es sólo acerca de la estructura. Se trata también de la idoneidad del software para las necesidades actuales. Por ejemplo un software que sabe cómo hervir el agua, cocinar arroz, verduras, frituras, y deje marinar un pollo no es complemento perfecto para alguién que sólo está interesado en conseguir una taza de té.

Si bien es probable que no te molesta que el motor de tu coche tiene dos válvulas adicionales en stand-by para las ocaciones en que se necesite aceleración extra, sin duda te molesta si es necesario cambiar todas las válvulas de tu motor. Ese es el costo de la manipulación escesiva.

Estas gastando dinero en el desarrollo de cosas que no es realmente necesario, y está gastando el dinero en tener que lidiar con la complejidad sobre la ingeniería de código durante el trabajo.

Una forma de pruebas que guíe el diseño de TDD es que te indican exactamente lo que si software debe ser capaz de hacer ahora. No mañana, no ayer, ahora. Proceder en estos pequeños pasos, implica que la funcionalidad de la aplicación sea suficiente como para conseguir el próximo paso de prueba. De esta manera estamos en control de nuestro software y su diseño. Y tenemos la seguridad neta de pruebas automatizadas para asegurarse de no tropezar en la oscuridad, tenemos una visión clara hacia donde debemos ir, y tenemos la confianza de que estamos implementando lo que importa y las cosas que se necesiten en este momento.

Este tema de centrarse en el presente es fundamental para TDD. De hecho, el tema se repite en el segundo paso del ciclo de TDD, son sólo escribir el código suficiente.

A continuación escribimos el código lo suficiente

En la segunda parte del ciclo TDD es escribir el código lo suficiente como para hacer correr la prueba. ¿Por qué sólo el código es suficiente? La prueba que hemos escrito es una prueba que está fallando. Se ha señalado una brecha entre lo que hace el código y lo que esperamos que haga. Es una pequeña brecha que debemos ser capaces de cerrar en pocos minutos, lo que a su vez, significa que el código nunca se rompe por mucho tiempo.

Una de las ideas fundamentales detrás del concepto de desarrollo de las pruebas primero es, mostrar lo que debe aplicar con el fin de progresar en el desarrollo el software. Usted no es sólo codificador a distancia, haciendo caso omiso a las exigencias de la pieza de código que estás escribiendo. Estas satisfaciendo un requesito explícito y sin ambiguedades expresda mediante una prueba. Estás haciendo progresos y, usted tiene una prueba a ejecutar y demostrar para él.

Vale la pena notar que, cuando escribimos sólo el código lo suficientemente, nuestro objetivo principal es hacer correr la prueba lo más rápidamente posible. Eso a menudo significa una implementación que no es óptima. Y eso está bien. Nosotros nos encargamos de todos los comportamientos deseados esten en su lugar y que las pruebas lo demuestren.Con las pruebas como red de seguridad, podemos proceder a mejorar el diseño en el último paso del ciclo TDD: refactorización.

Luego Refactorizamos

El último paso del ciclo de TDD es - refactorizar el código - es cuando damos un paso atrás, para mirar nuestro diseño, y descibrir la manera de hacerlo mejor. El paso es refactorización lo que hace que TDD sea sostenible. Podríamos considerar TDD sin refactorización para hacer reproducir código feo. Probado a fondo el código feo, que es directamente proporcional a la productividad en el trabajo con más desarrollo de código, no olvidar de refactorizar. De hecho, es tan importante que vamos a dedicar una sección entera para hablar de refactorización con mayores detalles.

Antes de ir a la refactorización, vamos a explorar el panorama del desarrollo de software en pequeños incrementos.

1.3.2 El desarrollo en pequeños incrementos

Un principio común de los métodos ágiles es que todos ellos indican una producción potencial. Independiente de la poca funcionalidad se debe sacar varias versiones del software en el día (algunos proyectos han informado de la construcción de un paquete de software varias veces al día) hasta que el proyecto esté terminado. Esta práctica garantiza que cuando el plazo llega, tienes algo que ofrecer. Puede que no tenga todo los requisitos que el cliente pidío, y puede que la iteración no tenga todo lo que dice el plan pero, al menos tiene algo y algo que funciona. La figura 1.6 muestra una progresión gradual de trabajo,la funcionalidad porbada en el inventario de los no integrados, pequeñas tareas determinadas en el tiempo.

Demasiados proyectos han retrasado su fecha de vencimiento una vez más consiguiendo su cancelación, sin entregar una sola línea de código de trabajo. Con la construcción de pequeños productos en forma incremental, iteración por iteración, usted no tiene que preocuparse de la fecha límite porque tiene un trabajo (aunque no complete) a partir de la primera iteración. Del mismo modo, demasiados proyectos han entregado el código con errores como consecuencia de una avalancha de última hora de hacer las cosas juntos de la fecha límite.

TDD elimina este problema en procesos de pequeños pasos, cada uno de los cuales resulta un producto más cerca de la conducta deseada. Porque estos pasos son tan pequeños (calculado en minutos en lugar de horas o días) que no termine con un montón de código aleatorio que necesitamos esamblar rápidamente. Nosotros debemos mantener actualizado el software para no mantenerlo demasiado alejado de su estado. De esta manera mantenemos el software ágil en vez de mirar a lo lejos.


Construir un software en incrementos y especialmente, en incrementos dictados por la percepción de costo y beneficio empresarial, no es algo que puedes hacer con el tradicional "diseñar todo desde el principio" , teniendo en cuenta cada posible movimiento, de modo que la arquitectura quede sólida como una roca, y que soporte todas los requisitos del producto teniendo "enfoque del diseño". No podemos construir la arquitectura completa, que es lo ideal para el producto final. Nosotros tenemos la necesidad de añadir una iteración, añadiendo a nuestro diseño un pequeño paso a la vez.

En la figura 1.7 muestra cómo esta iteración, se mueve en un proceso incremental de ida y vuelta, agregando pqueños pasos funcionalmente y adjustandolo a nuestro diseño- y arquitectura- para acomodar correctamente la funcionalidad agregada.

Este diseño es progresivo y evolutivo, en vez de diseñar hasta el final, diseñamos todo lo que se considera necesario para avanzar. En lugar de analizar a fondo los escenarios imaginables antes de finalizar el diseño, optamos por tomar nuestras desicsiomes de diseño basado en el conocimeinto no supuestos de la aplicación

El grado de diseño incial es necesario antes de sumergirse a especificar la capacidad de la aplicación varía de una situación a otra, entre los equipos y los individuos y entre tecnologías. La clave está en mantener una visión en la dirección correcta. Gran parte de su diseño no se puede arreglar? Reduzca el diseño incial. Terminó con un diseño que no escala lo suficiente? Gire la palanca de diseño incial a un nivel superior.


Usted problamente ha notado que seguimos habalndo de dar pequeños pasos. Vamos a echar un vistazo más de cerca que lo pequeño es bueno para nosotros.

Suficientemente pequeña para caber nuestras cabezas

La lógica de cortar una meta grande en pequeñas pruebas es doble. En primer lugar, muchas tareas que enfrentamos en el trabajo son demasiado grandes, ambiguas , o de plano complejo para ser manejable. Por lo tanto, tenemos que dividir en piezas mas pequeñas para que se ajusten mejor en la cabeza. No sé el tuyo, pero mi mente no controla grandes monstrous, y he oído a otros decir lo mismo. En la gura 1.8 muestra cómo un problema complejo se puede simplificarse en más pequeñas y simples problemas a resolver uno a la vez.

Vamos hacer frente. La mayoría de la gente puede manejar en su memoria hasta cinco o siete conceptos al mismo tiempo en el trabajo. Sobrecargar su cerebro va más alla de su capacidad que se ve obligado a dar lugar a pasar de alto ya que estamos cambiando cosas de ida y vuelta entre la memoria de trabajo y la memoria a largo plazo ( o escrito). Tras una secuencia de pequeños pasos hacia el objetivo final, nos permite medir nuestro avances en términos de algo correcto, en el supuesto de que sabremos cuando cada uno de esos pasos se terminó - porque hemos cortado nuestra tarea global en una secuencia de pequeñas pruebas que hacen progresos mensurables hacia el objetivo final.

TDD, por su naturaleza fomenta ese tipo de pequeños pasos con el diseño evolutivo. Estamos mejorando constantemente, cambiando el diseño en pequeños incrementos. En la práctica, vamos a construir una arquitectura a medida que avanzamos, sólo hay que tener en cuenta la tarea al estimar el trabajo

Echemos un vistazo más de cerca cómo funciona la evolución del diseño, cómo se crea la vida a base de código, y qué tipo de implicaciones que tiene para nuestra forma de trabajar.

Evolución del diseño

Muchos programadores están familiazados con una situación en la que un fragmento de código es arreglado por nadie. ¿Por qué? Porque ese trozo de código le sucede en la interfaz entre dos componentes y por tanto, es mucho más difícil para cambiar, así que nadie acaba de modificarlo. El diseño evolutivo es un modo de pensar que nos obliga a hacer ese cambio - alimentar la vida y el crecimeinto del código base, en lugar de protegerla del mundo errado, Lo errado necesita el cambio y por lo tanto mejorar la calidad de nuestro diseño, e indirectamente , la calidad de todo el sistema.

Entonces, ¿cómo funciona este diseño evolutivo? Se trabaja en pequeños pasos. La cantidad sugerida de diseño inicial varía de un método ágil a otro, pero la idea es que sólo se va a implementar la arquitectura que usted sabe que va a necesitar. Por ejemplo si se va a trabajar en un un servidor de correo electrónico. En ese momento, usted sabe que tendrá un servidor de correo , por lo que debe aplicar un componente se servicio de correo de arquitectura electrónico, instalación del servidor y así sucesivamente. Normalmente este cambio de arquitectura puede ser agregados en el tiempo sin causar demasiado dolor. A veces no es tan fácil.

Los sistemas de software pueden tener ciertas necesidades por lo general relacionados con el desempeño y/o capacidades de red que no puede ser fácil para agregar en la arquitectura después de hecho. Por ejemplo, la dicisión de una aplicación de escritorio de un sólo usuario en un cliente de escritorio hablando con un servidor multi-usuario en la red es algo muy trabajoso. Del mismo modo, hacer un soporte de procesamiento de aplicación por lotes en tiempo real , es una tarea no fácil de enfrentar.

Aunque los cambios en los requisitos son en general algo que no podemos prever, los desarrolladores suelen ver de antemano que cierta evolución va a tener lugar en algún momento, a la luz de los requisitos conocidos. Podríamos llamar a este cambio anticipado. Como muestra el gráfico 1.9 señala que el cambio esperado no se garantiza el cambio. Algunos cambio anticipado nunca sucede, y algunos podría convertirse rápidamente en imprevistos cambiar a medida que las incógnitas revelan diversas sorpresas



Un diseño evolucionado no significa usar un sentido común prohibido. Los desarrolladores deben hacer uso de lo que saben dentro dem los límites comunes, con la conciencia de la incertidumbre del cambio en sí mismo, y sin perder de mente cúales son las prioridades.

Por ejemplo, considere una situación en la que sabemos que, si actualmente el sistema necesita manejar una actualización por lotes por la red de la empresa gestionando las relaciones con clientes (CRM), y a pocos meses es probable que se requiera desarrollar con integración en tiempo real pasando mensajes de servicios web con protocolo de transferencia de hipertexto (HTTP). Con esta información ¿qué debemos hacer para dar cabida a los posibles necesidades futuras de integración en tiempo real?

¿Hay que separar la lógica de procesamiento de datos a partir de los datos de recepción de la lógica?. Por supuesto. En caso de construir el primer lanzamiento, donde se debe aplicar servicios de cajas en forma externa y donde el servicio web soporte la manipulación de mensaje y se convierte en relevante? Tal vez deberíamos , tal vez no.

El punto aquí es hacer un equilibrio entre evitar el trabajo innecesario de algo que no necesitamos y evitar tomar atajos, La historia ha demostrado una y otra vez que en su mayor parte la evolución del diseño es más barato que tratar de obtener en el principio el diseño final en papel.

Disciplina que se requiere

Una vez más, la cantidad de trabajo por adelantado varía de un proyecto a otro ( y debe ser porque un tamaño no se ajustan a todos)
sino que significa que el diseño evolutivo , en cualquier instancia estará haciendo un montón de cambios en el código existente para añadir nuevas características en el sistema en la medida que pasan las iteraciones.

Con una gran cantidad de cambios en el código en el tiempo, no podemos permitirnos tener el código mal escrito. Por lo tanto se debe tener mucha disciplina por parte de los desarrolladores para mantener nuestro código y que no se pudra. La buena noticia es que junto con su prácticas de apoyo, la evolución del diseño también nos ayudará a eliminar a esos problemas y, finalmente, estabilizar nuestro error evitando construir sobre cimientos de defectos.

¿Cuáles son esas prácticas de apoyo? En resumen, se trata del mantener la salud de su software en todo momento. Una parte esencial de lograr esto es la refactorización. Ya hemos señalado la refactorización como último paso del ciclo de TDD.

1.3.3 Mantener códigos sanos con refactorización.

Procedimiento en pequeños incrementos significa que constantemente estamos ampliando el sistema para hacer algo que su diseño actual no puede apoyar. En consecuencia, constantemente estamos ampliando el diseño del sistema de manera que pueda romper los conceptos existentes, así como introducir otros nuevos. Esto, asu vez , significa que estamos obligando a terminar un diseño roto que es incosistente, desequilibrado, difícil de entender, difícil de extender. Y eso podría dificultar seriamente nuestra capacidad para mantener la distribución de software a nuestros clientes. Sin embargo, hay una manera de practicar el diseño evolutivo sin dejar que el diseño se empobresca y se deteriore y eso se llama refactorización.

Citando a Martin Fowler, el autor de refactorización: mejorar el diseño de los códigos actuales (Addison- Wesley, 1999), la refactorización es "una técnica disciplinada para la reestructuración un organismo ya existente de código, alterando su estructura interna sin cambiar su comportamiento externo. "Esa descripción arregla para incluir una gran cantidad de informaicón en una frase corta. Vamos a explicar y ver lo que realmente nos dicen acerca refactorización".

Refactoring es disciplinado


Cuando hablamos de refactorización (el verbo), no sólo estamos alterando la estructura del código, sino que la mejora del diseño, alterando en una forma controlada mediante la aplicación de pequeñas transformaciones que se llaman refactorizaciones (en sustantivo). En otras palabras, la refactorización se trata de aplicar el código de refatorización de manera controlada. Está por ejemplo la reestructuración que puede ser un cambio significativo en el diseño actual, pero es siempre ir en pequeños pasos , verificando en cada etapa las transformaciones que se han hecho no cambien el comportamiento existente.

Lo importante no es sólo el cambio del código. En primer lugar, hay que identificar problemas específicos en el diseño, y luego refactorings adecuadas y aplicarlas cuidadosa e inteligentemente. En la manera antigua, esperamos hasta que un problema empieza a presentarse , y sólo entonces lo resolvemos. Nosotros predecimos los problemas de diseño de antemano y prepararse para ellos - que aumentar la posibilidad de crear más problemas con el diseño de nuestro sistema de resolverlos.

Las Refactorizaciones son transformaciones

Una refactorización se puede pensar como una transformación entre dos estados. Para el primer estado puede tener algunas características que a usted le gustaría deshacer o mejorar, y el estado representa un diseño que podria incorparse en esa mejora.
La razón para hecer esta refactorización puede ser, por ejemplo, que la subclase es sólo una extención de la superclase a fin de reutilizar una pequeña parte de esta funcionalidad y, como un indeseado efecto, hereda un montón de datos, que nosotros no sabemos cuidar. En la figura 10 muestra un ejemplo de una factorización llamada Sustituir Herencia con Delegación (documentado en el libro Fowler) que, como su nombre lo indica, mueve nuestro diseño de una hertencia de solución a una solución delegation-based.


Algunas de estas refactorizaciones pueder ser automatizadas con una herramienta moderna de desarrollo. Esta automatización pueder ser realizada con un diseño evolucionado de refactorización visible para aplicaciones y sistemas de cualquier tamaño y complejos (Podría imaginar renombrar un método con claridad si esto significa chequear una docena de archivos de control de versiones, haciendo una búsqueda, reemplazo y verificación de ellos? )

Refactorización para Patrones

A veces, uno o ambos estados nos movemos con nuestras refactorizaciones conocidas de patrones de diseños, o conocidas soluciones comunes a problemas de diseño. Aunque la mayoría de las refactorizaciones son aplicadas diariamente basadas en operaciones a nivel es mucho menor que en los patrones de diseño. Para leer más sobre la relación entre refactorización y patrones de diseño, se recomeinda a Joshua Kerievsky's Refactoring to Patterns (Addison-Wesley, 2004).

Refactorización altera la estructura interna


Así estas transformaciones son aplicadas para estructuras internas de sistemas- el código- lo que significa que muchas de las refactorizaciones son a bajo nivel. Por ejemplo, una de las refactorizaciones más comunes es llamada, "rename method". Renombrar un método o variable local tal vez no sea tan significativa en un cambio del diseño del sistema. pero renombrar un método de algo ambiguo a algo claro y conciso puede hacer un mundo de diferencia para alguén nuevo en el cñodigo y que necesita entender el código existente.

También, como la refactorización a bajo nivel, es fundamental la construcción de bloques, para lograr mayor refactorización. Esta es la mayor refactorización típica los que se relacionan con movimientos responsables alrededor del código, introduciendo o removiendo una jerarqía de herencia, o marcando otro cambio similar que (usualemente) involucra más de una clase. En fin todas las refactorizaciones pueden ser reducidas a una series de pequeños pasos, como renombrando una variable, agregando una nueva, clase vacía, cambiando el tipo de retorno de un método ; y asi sucesivamente.

Aunque, técnicamente la refactoriazión más comun estan pensadas en el renombre de métodos, El número de la razón para la refatorización es la duplicación, Esto significa duplicar en términos de obtener dos piezas similares de código en dos métodos o clases- algunos pueden ser extraídos dentro de métodos invocados en ambos lugares - pero, la duplicación podría ser en términos de responsabilidades. La duplicación, seguro que es malo para el código porque hace que el sistema sea más dificultoso a los cambios. Habiendo lógica y duplicación responsable en varios sitios es una manera garantizada de introducir defectos olviándonos de hacer cambios en todos los rincones necesarios del código base.

Además de no introducir defectos a nuestros cambios en la estructura interna del código, también, nuestra refactorización no debería agregar funcionalidades. Es decir, la refactorización pordría preservar el comportamiento.

La Refactorización preserva el comportamiento

La última parte de la anterior cita, Martin Fowler dice: "Sin Cambio (del código) comportamiento externo". Qué significa esto? Significa que, cualquier transformación aplicada a un código existente, esas transformaciones podría sólo afectar al diseño del código y a su estructura - no es externamente visible o funcionalmente. En otras palabras, el código de cliente con respecto al código refactorizado podría no notificar alguna diferencia en el comprotamiento.

Renombrando un método de una parte de la clase pública de una interfaz podría obviar tener réplica por medio del código del cliente, pero el comportamienro del método podría no cambiar el camino. Similarmente , reestructurando el comportamiento del código detrás de la interfaz pública podría no
debe en modo alguno cambiar el comportamiento visible a través interfaz del código público.

Nota: Ahora que conocemos qué es refactorización, vale la pena mencionar que no es.En los pocos años pasados. Comenzé a escuchar la palabra refactorización. Para mi desepción , en más casos , parece, la palabra correcta se han escrito o editado sin la disciplina asociada con la refactorización.

Si pensamos que hemos tenido éxito, cambiando la estructura interna sin cambiar el comportamiento externo. Calma. Pero como podemos saber que nuestra factorización no ha cambiado el comportamiento externo?. Porque los test corren sobre nuestro código, seguro¡. Queremos mantener nuestro software trabajando, y los test son el instrumento para garantizarlo.

Asegurar que el software todavía funciona.

Es fácil decir que podríamos entregar trabajo de software desde el primer día. pero puede parecer una ardua tarea para ser constantemente refactoring en el diseño, todo el tiempo asegurando que los cambios no alteren la funcionalidad existente. Si es desalentador. Es desalentador a tal grado que seguimos callendo si no tenemos ningún tipo de ayuda. Aunque el propósito principal del teste en TDD es ayudar a nuestros diseños y desarrollo, ellos tienen el rol de ejecutar en consonancia con el futuro.

Contrariamente a las creencias popular, el testing no necesita ser una carga. Sin embargo, es verdad que el testing manual puedeser más lento y propenso a errores, con los cual , los desarroladores de turno quedan fuera del testing general. La experiencia a demostrado que desarroladores (y tester dedicados igualmente de hecho) tienden a saltar algunos casos del testing manual, y fiarse de su intuición más que esos test que no pudiero haber sido errados por cualquier cosa que hagan cambiar su testing. Afortunadamente, estamos en el negocio de la automatización de software por lo tanto estamos mejor poscionados para ayudarnos a si mismos, hacer con la computadora de turno testing más rápido y automatizados.

Protección con los test automatizados

La regresión en general se trata de volver hacia atrás un estado de menos desarrollo. en el caso del software, la regresión es existente, funcionando funcionalmente obteniendo errores.
Eso es, regresar desde un estado en funcionamiento a un estado de no funcionando, como de ilustra en la figura 1.11.


Regresión no es justamente que suceda. Como desarrolladores provocamos regresión introduciendo defectos o cambios. Como profesionales de desarrollo de software queremos conocer los cambios que no provocan errores y queremos conocerlo tan pronto como sea posible. Esto sólo lo podemos llevar a cabo haciendo test automatizados que podemos ejecutar a voluntad y alertarnos de alguna falla durante la ejecución del test.

Es denominado testing de regresión. Se ejecutan los test repetidamente del proyecto, para verificar los defectos que existián, no han reaparecido nuevamente. Como el software ha evolucionado en miles de cambios - en otras palabras, un testing que no tiene regresión problamente no puede encontrar errores o nuevo bugs que de alguna manera puede aparecer.

Hay un número de caracteríticas que son importantes para los test automatizados, uno de los cuales el test puede ser de fácil ejecución- de otra manera podemos vernos tentados a saltarnos el test y chequear sus cambios esperando que los test nos avisen de alguna incidencia.

Otra propiedad esencial es que los test corran rápidamente- pero desde otro punto de vista podemos tentarnos a ejecutar los test pocos frecuentes, saltándonos pasos importantes en la verificaicón.

La no ejecución inmediata de los test, arriesga pérdida de tiempo en la obtención de un bug a última hora, a menos de tener como ventaja de conocer los cambios del bug. Una inmediata retroalimentación con el correcto contexto logra enormes resultados agilizando el desarrollo, prácticamente no es tiempo perdido en trabajo de investigsación tratando de resolver la solución exacta de un bug. Si ejecutamos nuestras suit de test después de pequeños cambios, conoceremos exactamente cúales son las líneas de código asociadas que causan la fala de los test.


Obteniedo una rápida retroalimentación.

Algunas veces, no es posible ejecutar todos los test lo suficientemente rápido. Esto es un callejón sin salida para los test automatizados que accesan a recursos externos como bases de datos, drive compartidos, servidor web , o directorios de servidores. Al igual que los archivos de sistemas pueden tomar varios minutos en la ejecución de sus test

A veces, hay algunos test que corren todas y largas las tareas por un desarrollador desocupado, aún si los test han sido optimizados una y otra vez.

En esta situación, a menudo tiene sentido ejecutar una subset de test ( un subtest es más frecuente para pillar algún bugs en algún cambio realizado) chequeando y permitiendo construir servicios ruidosos a través de restos de test mientras se ejecuta la siguiente tarea. Esos servicios son algunos referentes a servicios de integración contínua, por el hecho que ellos son amenudos usados en conjunto con la practica de integración contínua - desarrollos integrando sus cambios frecuentemente como pequeños incrementos.

Integración Contínua no es obtener contínuamente servicios integrados.

No hace falta Servidores de integración contínua para considerar con la práctica de contínua integración. Servicios de integración contínua provee de máquinas para automatizar construcciones y reportes selectos. Si los desarrolladores no están integrando sus cambios con frecuencias, el servicio de integración contínua no realiza nada, mientras que los desarrolladores continúan apartándose del uno del otro, contribuyendo a más confusos conflictos.

Acercarse básicamente con confianza a los últimos cambios que no tienen algún quiebre, acelera el desarrollo, Fiarse en la convicción de que más tiempo en el cambio no erra partes del sistema, no cubre los subtest seleccionados.

Podría pensar que es una forma óptima de obtener una rápida retroalimentación. Si el test no tiene fallas , procedemos a pasos máximos, pasando tiempo ejecutando los set sólo para encontrar que ninguno de ellos falla. Si uno de estos test construídos falla, detemos la ejecución hasta encotrar el contexto avanzando a la próxima tarea. Realizar los cambios- perdiendo el contexto puede significar una gran diferencia en el tiempo y el esfuerzo de un correcto error.

Una propiedad escencial para los test de regresión, es haber ejecutado repetidamente Esto significa que necesitamos alguien que invierta en tiempo para ejecutar y re ejecutar los test de regresión - cientos o miles - o podemos buscar una automatizarlo a todos ellos en el computador , ejecutándolos automáticamente o cuando se solicite con una mínimo involucración del humano.

Tenemos una idea de que trata TDD, qué hace , y por qué podríamos usarlo. Antes que nos tiremos de cabeza en los detalles de hacer test en el desarrollo detengámosnos en los capítulos 1 y 2 , para que exploremos un poquito de las técnicas futuras de los test de aceptación más allá y discutir cómo podemos obtener los test de aceptación como producto que necesitamos.

No hay comentarios:

Publicar un comentario