El desarrollo basado en pruebas ( TDD ) es una forma de escribir código que implica escribir un caso de prueba automatizado a nivel de unidad que falla, luego escribir solo el código suficiente para que la prueba pase, luego refactorizar tanto el código de prueba como el código de producción, y luego repetir con otro nuevo caso de prueba.
Los enfoques alternativos para escribir pruebas automatizadas son escribir todo el código de producción antes de comenzar con el código de prueba o escribir todo el código de prueba antes de comenzar con el código de producción. Con TDD, ambos se escriben juntos, lo que reduce los tiempos de depuración necesarios. [1]
TDD está relacionado con los conceptos de programación de prueba primero de la programación extrema , que comenzó en 1999, [2] pero más recientemente ha creado un interés más general por derecho propio. [3]
Los programadores también aplican el concepto para mejorar y depurar código heredado desarrollado con técnicas más antiguas. [4]
El ingeniero de software Kent Beck , a quien se le atribuye haber desarrollado o "redescubierto" [5] la técnica, afirmó en 2003 que TDD fomenta diseños simples e inspira confianza. [6]
La descripción original de TDD se encontraba en un antiguo libro sobre programación. Decía que se toma la cinta de entrada, se escribe manualmente la cinta de salida que se espera y luego se programa hasta que la cinta de salida real coincida con la salida esperada. Después de haber escrito el primer marco xUnit en Smalltalk, recordé haber leído esto y lo probé. Ese fue el origen de TDD para mí. Cuando describo TDD a programadores más antiguos, a menudo escucho: "Por supuesto. ¿De qué otra manera se podría programar?" Por lo tanto, me refiero a mi papel como "redescubridor" de TDD.
— Kent Beck , "¿Por qué Kent Beck se refiere al 'redescubrimiento' del desarrollo basado en pruebas? ¿Cuál es la historia del desarrollo basado en pruebas antes del redescubrimiento de Kent Beck?" [7]
Los pasos de TDD varían un poco según el autor en cuanto a cantidad y descripción , pero en general son los siguientes. Se basan en el libro Test-Driven Development by Example [6] y en el artículo Canon TDD de Kent Beck [8] .
Cada prueba debe ser pequeña y las confirmaciones deben realizarse con frecuencia. Si el código nuevo no supera algunas pruebas, el programador puede deshacerlas o revertirlas en lugar de depurar excesivamente.
Al utilizar bibliotecas externas , es importante no escribir pruebas que sean tan pequeñas como para probar efectivamente solo la biblioteca en sí, [3] a menos que haya alguna razón para creer que la biblioteca tiene errores o no es lo suficientemente rica en funciones para satisfacer todas las necesidades del software en desarrollo.
El TDD se ha adoptado fuera del desarrollo de software, tanto en equipos de productos como de servicios, como trabajo impulsado por pruebas . [9] Para que las pruebas sean exitosas, deben practicarse en los niveles micro y macro. Cada método en una clase, cada valor de datos de entrada, mensaje de registro y código de error, entre otros puntos de datos, deben probarse. [10] De manera similar al TDD, los equipos que no trabajan con software desarrollan controles de calidad (QC) (generalmente pruebas manuales en lugar de pruebas automatizadas) para cada aspecto del trabajo antes de comenzar. Estos controles de QC se utilizan luego para informar el diseño y validar los resultados asociados. Los seis pasos de la secuencia TDD se aplican con cambios semánticos menores:
Existen varios aspectos a tener en cuenta en el desarrollo basado en pruebas, por ejemplo, los principios de "manténlo simple, estúpido" ( KISS ) y " no lo vas a necesitar " (YAGNI). Al centrarse en escribir solo el código necesario para pasar las pruebas, los diseños a menudo pueden ser más limpios y claros que los que se logran con otros métodos. [6] En Test-Driven Development by Example , Kent Beck también sugiere el principio " finge hasta que lo logres ".
Para lograr un concepto de diseño avanzado, como un patrón de diseño , se escriben pruebas que generan ese diseño. El código puede seguir siendo más simple que el patrón de destino, pero aún así pasa todas las pruebas requeridas. Esto puede resultar desconcertante al principio, pero permite que el desarrollador se concentre solo en lo que es importante.
Escribir las pruebas primero: Las pruebas deben escribirse antes de la funcionalidad que se va a probar. Se ha afirmado que esto tiene muchos beneficios. Ayuda a garantizar que la aplicación esté escrita para que sea testeable, ya que los desarrolladores deben considerar cómo probar la aplicación desde el principio en lugar de agregarla más tarde. También garantiza que se escriban pruebas para cada característica. Además, escribir las pruebas primero conduce a una comprensión más profunda y temprana de los requisitos del producto, garantiza la efectividad del código de prueba y mantiene un enfoque continuo en la calidad del software . [11] Al escribir código que prioriza las características, existe una tendencia por parte de los desarrolladores y las organizaciones a empujar al desarrollador a la siguiente característica, incluso descuidando las pruebas por completo. Es posible que la primera prueba TDD ni siquiera se compile al principio, porque es posible que las clases y los métodos que requiere aún no existan. Sin embargo, esa primera prueba funciona como el comienzo de una especificación ejecutable. [12]
Cada caso de prueba falla inicialmente: esto garantiza que la prueba realmente funciona y puede detectar un error. Una vez que esto se demuestra, se puede implementar la funcionalidad subyacente. Esto ha llevado al "mantra del desarrollo impulsado por pruebas", que es "rojo/verde/refactorización", donde rojo significa fallar y verde significa aprobar . El desarrollo impulsado por pruebas repite constantemente los pasos de agregar casos de prueba que fallan, aprobarlos y refactorizar. Recibir los resultados de prueba esperados en cada etapa refuerza el modelo mental del código del desarrollador, aumenta la confianza y aumenta la productividad.
El código de prueba necesita acceso al código que está probando, pero la prueba no debe comprometer los objetivos de diseño normales, como el ocultamiento de información , la encapsulación y la separación de intereses . Por lo tanto, el código de prueba unitaria generalmente se encuentra en el mismo proyecto o módulo que el código que se está probando.
En el diseño orientado a objetos, esto todavía no proporciona acceso a datos y métodos privados. Por lo tanto, puede ser necesario un trabajo adicional para las pruebas unitarias. En Java y otros lenguajes, un desarrollador puede usar la reflexión para acceder a campos y métodos privados. [13] Alternativamente, se puede usar una clase interna para contener las pruebas unitarias de modo que tengan visibilidad de los miembros y atributos de la clase envolvente. En .NET Framework y algunos otros lenguajes de programación, se pueden usar clases parciales para exponer métodos y datos privados para que las pruebas accedan a ellos.
Es importante que estos trucos de prueba no permanezcan en el código de producción. En C y otros lenguajes, se pueden colocar directivas del compilador como #if DEBUG ... #endif
alrededor de estas clases adicionales y, de hecho, de todo el código relacionado con las pruebas para evitar que se compilen en el código publicado. Esto significa que el código publicado no es exactamente el mismo que el que se probó unitariamente. La ejecución regular de menos pruebas de integración, pero más completas y de extremo a extremo, en la versión final puede garantizar (entre otras cosas) que no exista ningún código de producción que dependa sutilmente de aspectos del conjunto de pruebas.
Existe cierto debate entre los practicantes de TDD, documentado en sus blogs y otros escritos, sobre si es prudente probar métodos y datos privados de todos modos. Algunos sostienen que los miembros privados son un mero detalle de implementación que puede cambiar y que se les debería permitir hacerlo sin alterar el número de pruebas. Por lo tanto, debería ser suficiente probar cualquier clase a través de su interfaz pública o a través de su interfaz de subclase, que algunos lenguajes llaman la interfaz "protegida". [14] Otros dicen que los aspectos cruciales de la funcionalidad se pueden implementar en métodos privados y que probarlos directamente ofrece la ventaja de pruebas unitarias más pequeñas y directas. [15] [16]
Las pruebas unitarias se denominan así porque cada una prueba una unidad de código. Un módulo complejo puede tener mil pruebas unitarias y un módulo simple puede tener solo diez. Las pruebas unitarias utilizadas para TDD nunca deben cruzar los límites de proceso en un programa, y mucho menos las conexiones de red. Hacerlo introduce demoras que hacen que las pruebas se ejecuten lentamente y desalientan a los desarrolladores a ejecutar todo el conjunto. La introducción de dependencias en módulos o datos externos también convierte las pruebas unitarias en pruebas de integración . Si un módulo se comporta mal en una cadena de módulos interrelacionados, no está tan claro de inmediato dónde buscar la causa del fallo.
Cuando el código en desarrollo depende de una base de datos, un servicio web o cualquier otro proceso o servicio externo, imponer una separación que permita realizar pruebas unitarias también es una oportunidad y una fuerza impulsora para diseñar un código más modular, más comprobable y más reutilizable. [17] Son necesarios dos pasos:
Los métodos de objetos falsos y simulados que devuelven datos, aparentemente de un almacén de datos o de un usuario, pueden ayudar al proceso de prueba al devolver siempre los mismos datos realistas en los que las pruebas pueden confiar. También se pueden configurar en modos de falla predefinidos para que se puedan desarrollar rutinas de manejo de errores y probarlas de manera confiable. En un modo de falla, un método puede devolver una respuesta inválida, incompleta o nula , o puede lanzar una excepción . Los servicios falsos que no sean almacenes de datos también pueden ser útiles en TDD: un servicio de cifrado falso puede, de hecho, no cifrar los datos pasados; un servicio de números aleatorios falso siempre puede devolver 1. Las implementaciones falsas o simuladas son ejemplos de inyección de dependencia .
Un doble de prueba es una capacidad específica de la prueba que sustituye a una capacidad del sistema, normalmente una clase o función, de la que depende la UUT. Hay dos momentos en los que se pueden introducir dobles de prueba en un sistema: enlace y ejecución. La sustitución en el momento del enlace es cuando el doble de prueba se compila en el módulo de carga, que se ejecuta para validar la prueba. Este enfoque se utiliza normalmente cuando se ejecuta en un entorno distinto del entorno de destino que requiere dobles para el código de nivel de hardware para la compilación. La alternativa a la sustitución del enlazador es la sustitución en tiempo de ejecución en la que la funcionalidad real se reemplaza durante la ejecución de un caso de prueba. Esta sustitución se realiza normalmente mediante la reasignación de punteros de función conocidos o el reemplazo de objetos.
Los dobles de prueba son de distintos tipos y de variada complejidad:
Un corolario de esta inyección de dependencia es que la base de datos real u otro código de acceso externo nunca es probado por el propio proceso TDD. Para evitar errores que pueden surgir de esto, se necesitan otras pruebas que instancian el código controlado por pruebas con las implementaciones "reales" de las interfaces analizadas anteriormente. Estas son pruebas de integración y son bastante independientes de las pruebas unitarias TDD. Hay menos de ellas y deben ejecutarse con menos frecuencia que las pruebas unitarias. No obstante, pueden implementarse utilizando el mismo marco de pruebas.
Las pruebas de integración que alteran cualquier base de datos o almacenamiento persistente siempre deben diseñarse cuidadosamente teniendo en cuenta el estado inicial y final de los archivos o la base de datos, incluso si alguna prueba falla. Esto se logra a menudo utilizando alguna combinación de las siguientes técnicas:
TearDown
método que es parte integral de muchos marcos de prueba.try...catch...finally
estructuras de manejo de excepciones cuando estén disponibles.En el caso de TDD, una unidad se define más comúnmente como una clase o un grupo de funciones relacionadas, a menudo denominado módulo. Se afirma que mantener las unidades relativamente pequeñas ofrece ventajas fundamentales, entre ellas:
Las prácticas avanzadas de desarrollo basado en pruebas pueden llevar al desarrollo basado en pruebas de aceptación (ATDD, por sus siglas en inglés) y a la especificación por ejemplo, donde los criterios especificados por el cliente se automatizan en pruebas de aceptación, que luego impulsan el proceso tradicional de desarrollo basado en pruebas unitarias (UTDD, por sus siglas en inglés). [18] Este proceso garantiza que el cliente tenga un mecanismo automatizado para decidir si el software cumple con sus requisitos. Con ATDD, el equipo de desarrollo ahora tiene un objetivo específico que satisfacer (las pruebas de aceptación), lo que los mantiene continuamente enfocados en lo que el cliente realmente quiere de cada historia de usuario.
Un diseño eficaz de un caso de prueba garantiza que se completen todas las acciones necesarias, mejora la legibilidad del caso de prueba y facilita el flujo de ejecución. Una estructura coherente ayuda a crear un caso de prueba autodocumentado. Una estructura que se aplica comúnmente para los casos de prueba tiene (1) configuración, (2) ejecución, (3) validación y (4) limpieza.
Algunas de las mejores prácticas que una persona podría seguir serían separar la lógica común de configuración y desmontaje en servicios de soporte de pruebas utilizados por los casos de prueba adecuados, para mantener cada oráculo de prueba centrado únicamente en los resultados necesarios para validar su prueba y para diseñar pruebas relacionadas con el tiempo para permitir la tolerancia para la ejecución en sistemas operativos que no sean de tiempo real. La práctica común de permitir un margen de entre el 5 y el 10 por ciento para la ejecución tardía reduce la cantidad potencial de falsos negativos en la ejecución de pruebas. También se sugiere tratar el código de prueba con el mismo respeto que el código de producción. El código de prueba debe funcionar correctamente tanto para casos positivos como negativos, durar mucho tiempo y ser legible y mantenible. Los equipos pueden reunirse y revisar las pruebas y las prácticas de prueba para compartir técnicas efectivas y detectar malos hábitos. [19]
El desarrollo impulsado por pruebas está relacionado con el desarrollo impulsado por pruebas de aceptación (ATDD), pero es diferente. [20] TDD es principalmente una herramienta para desarrolladores que ayuda a crear una unidad de código bien escrita (función, clase o módulo) que realice correctamente un conjunto de operaciones. ATDD es una herramienta de comunicación entre el cliente, el desarrollador y el evaluador para garantizar que los requisitos estén bien definidos. TDD requiere automatización de pruebas. ATDD no, aunque la automatización ayuda con las pruebas de regresión. Las pruebas utilizadas en TDD a menudo se pueden derivar de pruebas ATDD, ya que las unidades de código implementan alguna parte de un requisito. Las pruebas ATDD deben ser legibles por el cliente. Las pruebas TDD no necesitan serlo.
BDD ( desarrollo impulsado por el comportamiento ) combina prácticas de TDD y de ATDD. [21] Incluye la práctica de escribir pruebas primero, pero se centra en pruebas que describen el comportamiento, en lugar de pruebas que prueban una unidad de implementación. Herramientas como JBehave, Cucumber , Mspec y Specflow proporcionan sintaxis que permiten a los propietarios de productos, desarrolladores e ingenieros de pruebas definir juntos los comportamientos que luego se pueden traducir en pruebas automatizadas.
Hay muchos marcos y herramientas de prueba que son útiles en TDD.
Los desarrolladores pueden utilizar marcos de prueba asistidos por computadora , comúnmente denominados colectivamente xUnit (que se derivan de SUnit, creado en 1998), para crear y ejecutar automáticamente los casos de prueba. Los marcos xUnit proporcionan capacidades de validación de pruebas de estilo de aserción e informes de resultados. Estas capacidades son fundamentales para la automatización, ya que trasladan la carga de la validación de la ejecución de una actividad de posprocesamiento independiente a una que se incluye en la ejecución de la prueba. El marco de ejecución proporcionado por estos marcos de prueba permite la ejecución automática de todos los casos de prueba del sistema o varios subconjuntos junto con otras características. [22]
Los marcos de prueba pueden aceptar resultados de pruebas unitarias en el protocolo Test Anything, independiente del lenguaje, creado en 1987.
Para poner en práctica TDD en sistemas grandes y complejos se necesita una arquitectura modular, componentes bien definidos con interfaces publicadas y una estructura de sistemas disciplinada en capas que maximice la independencia de la plataforma. Estas prácticas comprobadas permiten aumentar la capacidad de prueba y facilitan la aplicación de la automatización de pruebas y compilaciones. [11]
Los sistemas complejos requieren una arquitectura que cumpla con una serie de requisitos. Un subconjunto clave de estos requisitos incluye el soporte para la prueba completa y eficaz del sistema. Un diseño modular eficaz produce componentes que comparten características esenciales para un TDD eficaz.
Una técnica clave para construir una arquitectura modular eficaz es el modelado de escenarios, en el que se construye un conjunto de diagramas de secuencia, cada uno de ellos centrado en un único escenario de ejecución a nivel de sistema. El modelo de escenarios proporciona un vehículo excelente para crear la estrategia de interacciones entre componentes en respuesta a un estímulo específico. Cada uno de estos modelos de escenarios sirve como un conjunto completo de requisitos para los servicios o funciones que un componente debe proporcionar, y también dicta el orden en el que estos componentes y servicios interactúan entre sí. El modelado de escenarios puede facilitar en gran medida la construcción de pruebas TDD para un sistema complejo. [11]
En un sistema más grande, el impacto de la mala calidad de los componentes se ve magnificado por la complejidad de las interacciones. Esta magnificación hace que los beneficios de TDD se acumulen incluso más rápido en el contexto de proyectos más grandes. Sin embargo, la complejidad de la población total de pruebas puede convertirse en un problema en sí misma, erosionando las ganancias potenciales. Parece simple, pero un paso inicial clave es reconocer que el código de prueba también es software importante y debe producirse y mantenerse con el mismo rigor que el código de producción.
La creación y gestión de la arquitectura del software de prueba dentro de un sistema complejo es tan importante como la arquitectura del producto principal. Los controladores de prueba interactúan con la UUT, los dobles de prueba y el marco de pruebas unitarias. [11]
El desarrollo guiado por pruebas (TDD) es un enfoque de desarrollo de software en el que las pruebas se escriben antes que el código real. Ofrece varias ventajas:
Sin embargo, el TDD no está exento de inconvenientes:
Un estudio de 2005 concluyó que el uso de TDD implicaba escribir más pruebas y, a su vez, los programadores que escribían más pruebas tendían a ser más productivos. [25] Las hipótesis relacionadas con la calidad del código y una correlación más directa entre TDD y productividad no fueron concluyentes. [26]
Los programadores que utilizan TDD puro en proyectos nuevos (" greenfield ") informaron que rara vez sintieron la necesidad de invocar un depurador . Utilizado junto con un sistema de control de versiones , cuando las pruebas fallan inesperadamente, revertir el código a la última versión que pasó todas las pruebas puede ser a menudo más productivo que la depuración. [27]
El desarrollo basado en pruebas ofrece más que una simple validación de la corrección, sino que también puede guiar el diseño de un programa. [28] Al centrarse primero en los casos de prueba, uno debe imaginar cómo los clientes utilizan la funcionalidad (en el primer caso, los casos de prueba). Por lo tanto, el programador se preocupa por la interfaz antes de la implementación. Este beneficio es complementario al diseño por contrato , ya que aborda el código a través de casos de prueba en lugar de a través de afirmaciones matemáticas o preconcepciones.
El desarrollo basado en pruebas ofrece la posibilidad de realizar pequeños pasos cuando es necesario. Permite que el programador se centre en la tarea en cuestión, ya que el primer objetivo es que la prueba se apruebe. Los casos excepcionales y el manejo de errores no se tienen en cuenta inicialmente, y las pruebas para crear estas circunstancias ajenas se implementan por separado. De esta manera, el desarrollo basado en pruebas garantiza que todo el código escrito esté cubierto por al menos una prueba. Esto proporciona al equipo de programación y a los usuarios posteriores un mayor nivel de confianza en el código.
Si bien es cierto que se requiere más código con TDD que sin TDD debido al código de prueba unitaria, el tiempo total de implementación del código podría ser más corto según un modelo de Müller y Padberg. [29] Una gran cantidad de pruebas ayuda a limitar la cantidad de defectos en el código. La naturaleza temprana y frecuente de las pruebas ayuda a detectar defectos en las primeras etapas del ciclo de desarrollo, lo que evita que se conviertan en problemas endémicos y costosos. La eliminación de defectos en las primeras etapas del proceso generalmente evita una depuración prolongada y tediosa más adelante en el proyecto.
El TDD puede generar un código más modularizado, flexible y extensible. Este efecto suele producirse porque la metodología requiere que los desarrolladores piensen en el software en términos de pequeñas unidades que se pueden escribir y probar de forma independiente e integrar posteriormente. Esto genera clases más pequeñas y centradas, un acoplamiento más flexible e interfaces más claras. El uso del patrón de diseño de objetos simulados también contribuye a la modularización general del código porque este patrón requiere que el código se escriba de forma que los módulos se puedan cambiar fácilmente entre versiones simuladas para pruebas unitarias y versiones "reales" para la implementación.
Como no se escribe más código del necesario para superar un caso de prueba fallido, las pruebas automatizadas tienden a cubrir todas las rutas de código. Por ejemplo, para que un desarrollador de TDD añada una else
rama a una if
declaración existente, primero tendría que escribir un caso de prueba fallido que motive la rama. Como resultado, las pruebas automatizadas resultantes de TDD tienden a ser muy exhaustivas: detectan cualquier cambio inesperado en el comportamiento del código. Esto detecta problemas que pueden surgir cuando un cambio posterior en el ciclo de desarrollo altera inesperadamente otra funcionalidad.
Madeyski [30] proporcionó evidencia empírica (a través de una serie de experimentos de laboratorio con más de 200 desarrolladores) con respecto a la superioridad de la práctica TDD sobre el enfoque tradicional Test-Last o el enfoque de prueba de corrección, con respecto al menor acoplamiento entre objetos (CBO). El tamaño del efecto medio representa un efecto mediano (pero cercano a grande) sobre la base del metanálisis de los experimentos realizados, lo que es un hallazgo sustancial. Sugiere una mejor modularización (es decir, un diseño más modular), una reutilización y prueba más sencillas de los productos de software desarrollados debido a la práctica de programación TDD. [30] Madeyski también midió el efecto de la práctica TDD en las pruebas unitarias utilizando la cobertura de rama (BC) y el indicador de puntuación de mutación (MSI), [31] [32] [33] que son indicadores de la minuciosidad y la eficacia de detección de fallas de las pruebas unitarias, respectivamente. El tamaño del efecto de TDD en la cobertura de rama fue de tamaño mediano y, por lo tanto, se considera un efecto sustancial. [30] Estos hallazgos se han confirmado posteriormente mediante evaluaciones experimentales adicionales y más pequeñas de TDD. [34] [35] [36] [37]
El desarrollo basado en pruebas no realiza pruebas suficientes en situaciones en las que se requieren pruebas funcionales completas para determinar el éxito o el fracaso, debido al uso extensivo de pruebas unitarias. [38] Ejemplos de estos son las interfaces de usuario , los programas que funcionan con bases de datos y algunos que dependen de configuraciones de red específicas . TDD alienta a los desarrolladores a colocar la cantidad mínima de código en dichos módulos y a maximizar la lógica que se encuentra en el código de biblioteca comprobable, utilizando falsificaciones y simulaciones para representar el mundo exterior. [39]
El apoyo de la dirección es esencial. Si toda la organización no cree que el desarrollo basado en pruebas va a mejorar el producto, la dirección puede pensar que el tiempo que se dedica a escribir pruebas es una pérdida de tiempo. [40]
Las pruebas unitarias creadas en un entorno de desarrollo basado en pruebas suelen ser creadas por el desarrollador que escribe el código que se está probando. Por lo tanto, las pruebas pueden compartir puntos ciegos con el código: si, por ejemplo, un desarrollador no se da cuenta de que se deben verificar ciertos parámetros de entrada, lo más probable es que ni la prueba ni el código verifiquen esos parámetros. Otro ejemplo: si el desarrollador malinterpreta los requisitos del módulo que está desarrollando, el código y las pruebas unitarias que escriba serán incorrectos de la misma manera. Por lo tanto, las pruebas pasarán, lo que dará una falsa sensación de corrección.
Una gran cantidad de pruebas unitarias aprobadas puede generar una falsa sensación de seguridad, lo que resulta en menos actividades de pruebas de software adicionales , como pruebas de integración y pruebas de cumplimiento .
Las pruebas se convierten en parte de la sobrecarga de mantenimiento de un proyecto. Las pruebas mal escritas, por ejemplo, las que incluyen cadenas de error codificadas de forma rígida, son propensas a fallar y su mantenimiento es costoso. Esto es especialmente cierto en el caso de las pruebas frágiles. [41] Existe el riesgo de que las pruebas que generan errores falsos con regularidad se ignoren, de modo que cuando se produzca un error real, es posible que no se detecte. Es posible escribir pruebas que requieran poco y fácil mantenimiento, por ejemplo, mediante la reutilización de cadenas de error, y esto debería ser un objetivo durante la fase de refactorización del código descrita anteriormente.
Escribir y mantener una cantidad excesiva de pruebas requiere tiempo. Además, los módulos más flexibles (con pruebas limitadas) pueden aceptar nuevos requisitos sin necesidad de cambiar las pruebas. Por esos motivos, las pruebas para condiciones extremas o una muestra pequeña de datos pueden ser más fáciles de ajustar que un conjunto de pruebas muy detalladas.
El nivel de cobertura y detalle de las pruebas que se logra durante los ciclos repetidos de TDD no se puede recrear fácilmente en una fecha posterior. Por lo tanto, estas pruebas originales o tempranas se vuelven cada vez más valiosas a medida que pasa el tiempo. La táctica es solucionarlo pronto. Además, si una arquitectura deficiente, un diseño deficiente o una estrategia de pruebas deficiente conducen a un cambio tardío que hace que decenas de pruebas existentes fallen, entonces es importante que se solucionen individualmente. Simplemente eliminarlas, deshabilitarlas o alterarlas precipitadamente puede generar fallas indetectables en la cobertura de las pruebas.
La primera conferencia TDD se celebró durante julio de 2021. [42] Las conferencias se grabaron en YouTube [43]
Descubrimos que los estudiantes que primero hacían pruebas, en promedio, escribieron más pruebas y, a su vez, los estudiantes que escribieron más pruebas tendían a ser más productivos.
Por lo tanto, la relación de TDD con la calidad es problemática en el mejor de los casos. Su relación con la productividad es más interesante. Espero que haya un estudio de seguimiento porque las cifras de productividad simplemente no me cuadran muy bien. Existe una correlación innegable entre la productividad y la cantidad de pruebas, pero esa correlación es en realidad más fuerte en el grupo sin TDD (que tuvo un solo
valor atípico
en comparación con aproximadamente la mitad del grupo TDD que estaba fuera de la banda del 95%).
Si comparamos [TDD] con el enfoque de desarrollo no basado en pruebas, estamos reemplazando toda la comprobación mental y el depurador por código que verifica que nuestro programa hace exactamente lo que pretendíamos que hiciera.