stringtranslate.com

Desarrollo basado en pruebas

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 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.

TDD está relacionado con los conceptos de programación extrema de prueba primero , iniciados en 1999, [1] pero más recientemente ha creado un interés más general por derecho propio. [2]

Los programadores también aplican el concepto para mejorar y depurar código heredado desarrollado con técnicas más antiguas. [3]

Historia

El ingeniero de software Kent Beck , a quien se le atribuye haber desarrollado o "redescubierto" [4] la técnica, afirmó en 2003 que TDD fomenta diseños simples e inspira confianza. [5]

La descripción original de TDD estaba en un libro antiguo sobre programación. Decía que toma la cinta de entrada, escribe manualmente la cinta de salida que espera y luego programa hasta que la cinta de salida real coincida con la salida esperada. Después de escribir 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 mayores, a menudo escucho: "Por supuesto. ¿De qué otra manera podrías programar?" Por lo tanto, me refiero a mi papel como el de "redescubrir" 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?, Kent Beck (11 de mayo de 2012). "¿Por qué Kent Beck se refiere al "redescubrimiento" del desarrollo basado en pruebas?" . Consultado el 1 de diciembre de 2014 .

Ciclo de codificación

Una representación gráfica del ciclo de vida del desarrollo basado en pruebas.

Los pasos de TDD varían algo según el autor en cuanto a recuento y descripción, pero generalmente son los siguientes. Estos se basan en el libro Test-Driven Development by Ejemplo , [5] y el artículo Canon TDD de Kent Beck.

1. Enumerar escenarios para la nueva función.
Enumere las variantes esperadas en el nuevo comportamiento. "Existe el caso básico y luego, ¿qué pasa si este servicio se agota y si la clave aún no está en la base de datos?". El desarrollador puede descubrir estas especificaciones preguntando sobre casos de uso e historias de usuarios . Un beneficio clave de TDD es que hace que el desarrollador se centre en los requisitos antes de escribir el código. Esto contrasta con la práctica habitual, donde las pruebas unitarias sólo se escriben después del código.
2. Escribe una prueba para un elemento de la lista.
Escriba una prueba automatizada que pase si se cumple la variante en el nuevo comportamiento.
3. Ejecute todas las pruebas. La nueva prueba debería fallar , por razones esperadas
Esto muestra que realmente se necesita un nuevo código para la función deseada. Valida que el arnés de prueba esté funcionando correctamente. Descarta la posibilidad de que la nueva prueba sea defectuosa y siempre pase.
4. Escribe el código más simple que pase la nueva prueba.
Se aceptan códigos poco elegantes y codificados de forma rígida . El código se perfeccionará en el Paso 6. No se debe agregar ningún código más allá de la funcionalidad probada.
5. Todas las pruebas ahora deberían pasar
Si alguna falla, corrija las pruebas fallidas con cambios mínimos hasta que todas pasen.
6. Refactorice según sea necesario mientras garantiza que todas las pruebas sigan pasando
El código se refactoriza para facilitar la lectura y el mantenimiento. En particular, los datos de prueba codificados deben eliminarse del código de producción. La ejecución del conjunto de pruebas después de cada refactorización garantiza que no se interrumpa ninguna funcionalidad existente. Ejemplos de refactorización:
Repetir
Repita el proceso, comenzando en el paso 2, con cada prueba de la lista hasta que todas las pruebas estén implementadas y aprobadas.

Cada prueba debe ser pequeña y las confirmaciones deben realizarse con frecuencia. Si el nuevo código falla en algunas pruebas, el programador puede deshacerlo o revertirlo en lugar de depurarlo excesivamente.

Cuando se utilizan bibliotecas externas , es importante no escribir pruebas que sean tan pequeñas como para probar de manera efectiva simplemente la biblioteca misma, [2] a menos que haya alguna razón para creer que la biblioteca tiene errores o no tiene suficientes funciones para servir a todas las funciones. necesidades del software en desarrollo.

Trabajo basado en pruebas

TDD se ha adoptado fuera del desarrollo de software, tanto en equipos de productos como de servicios, como trabajo basado en pruebas . [6] Para que las pruebas sean exitosas, es necesario practicarlas en los niveles micro y macro. Es necesario probar cada método de una clase, cada valor de datos de entrada, mensaje de registro y código de error, entre otros puntos de datos. [7] De manera similar a TDD, los equipos que no son de 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 calidad 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:

  1. "Agregar una verificación" reemplaza "Agregar una prueba"
  2. "Ejecutar todas las comprobaciones" reemplaza "Ejecutar todas las pruebas"
  3. "Haz el trabajo" reemplaza "Escribe un código"
  4. "Ejecutar todas las comprobaciones" reemplaza "Ejecutar pruebas"
  5. "Limpiar el trabajo" reemplaza "Refactorizar código"
  6. "Repetir"

Estilo de desarrollo

Hay varios aspectos del uso del desarrollo basado en pruebas, por ejemplo, los principios de "mantenlo simple, estúpido" ( KISS ) y " No lo necesitarás " (YAGNI). Al centrarse en escribir sólo 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. [5] En Desarrollo basado en pruebas con el ejemplo , Kent Beck también sugiere el principio " Fíngelo hasta que lo logres ".

Para lograr algún 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í pasar todas las pruebas requeridas. Esto puede resultar inquietante al principio, pero permite al desarrollador centrarse sólo en lo 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 su capacidad de prueba, 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 . [8] Al escribir código de función primero, los desarrolladores y las organizaciones tienden a presionar al desarrollador a pasar a la siguiente función, 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 métodos que requiere aún no existan. Sin embargo, esa primera prueba funciona como el comienzo de una especificación ejecutable. [9]

Cada caso de prueba falla inicialmente: esto garantiza que la prueba realmente funcione y pueda detectar un error. Una vez que se muestra esto, se puede implementar la funcionalidad subyacente. Esto ha llevado al "mantra del desarrollo basado en pruebas", que es "rojo/verde/refactor", donde rojo significa falla y verde significa aprobado . El desarrollo basado en 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.

Visibilidad del código

El código de prueba necesita acceso al código que está probando, pero las pruebas no deben comprometer los objetivos de diseño normales, como la ocultación de información , la encapsulación y la separación de preocupaciones . 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 trabajo adicional para las pruebas unitarias. En Java y otros lenguajes, un desarrollador puede utilizar la reflexión para acceder a campos y métodos privados. [10] Alternativamente, se puede usar una clase interna para contener las pruebas unitarias para que tengan visibilidad de los miembros y atributos de la clase adjunta. En .NET Framework y algunos otros lenguajes de programación, se pueden usar clases parciales para exponer métodos y datos privados a los que puedan acceder las pruebas.

Es importante que estos trucos de prueba no permanezcan en el código de producción. En C y otros lenguajes, directivas del compilador como #if DEBUG ... #endifse pueden colocar alrededor de dichas clases adicionales y, de hecho, de todo el resto del 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ó en la unidad. La ejecución regular de menos pruebas de integración, pero más completas, 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 profesionales de TDD, documentado en sus blogs y otros escritos, sobre si es prudente probar métodos y datos privados de todos modos. Algunos argumentan que los miembros privados son un mero detalle de implementación que puede cambiar, y se les debe 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 de su interfaz de subclase, que algunos lenguajes llaman interfaz "protegida". [11] Otros dicen que aspectos cruciales de la funcionalidad se pueden implementar en métodos privados y probarlos directamente ofrece la ventaja de pruebas unitarias más pequeñas y directas. [12] [13]

Fakes, simulacros y pruebas de integración.

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 sólo diez. Las pruebas unitarias utilizadas para TDD nunca deben cruzar los límites del proceso en un programa, y ​​mucho menos las conexiones de red. Hacerlo introduce retrasos que hacen que las pruebas se ejecuten lentamente y disuaden a los desarrolladores de ejecutar toda la suite. 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 queda tan claro dónde buscar la causa del fallo.

Cuando el código en desarrollo se basa en una base de datos, un servicio web o cualquier otro proceso o servicio externo, imponer una separación comprobable por unidad también es una oportunidad y una fuerza impulsora para diseñar código más modular, más comprobable y más reutilizable. [14] Son necesarios dos pasos:

  1. Siempre que se necesite acceso externo en el diseño final, se debe definir una interfaz que describa el acceso disponible. Consulte el principio de inversión de dependencia para conocer los beneficios de hacer esto independientemente del TDD.
  2. La interfaz debe implementarse de dos maneras, una de las cuales realmente accede al proceso externo y la otra es falsa o simulada . Los objetos falsos necesitan hacer poco más que agregar un mensaje como "Persona objeto guardado" a un registro de seguimiento , contra el cual se puede ejecutar una afirmación de prueba para verificar el comportamiento correcto. Los objetos simulados se diferencian en que ellos mismos contienen afirmaciones de prueba que pueden hacer que la prueba falle, por ejemplo, si el nombre de la persona y otros datos no son los esperados.

Los métodos de objetos falsos y simulados que devuelven datos, aparentemente de un almacén de datos o de un usuario, pueden ayudar en el proceso de prueba al devolver siempre los mismos datos realistas en los que pueden confiar las pruebas. También se pueden configurar en modos de falla predefinidos para que se puedan desarrollar y probar de manera confiable rutinas de manejo de errores. En un modo de error, un método puede devolver una respuesta no válida, incompleta o nula , o puede generar una excepción . Los servicios falsos distintos de los almacenes de datos también pueden ser útiles en TDD: es posible que, de hecho, un servicio de cifrado falso no cifre 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 .

Una prueba doble 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 del tiempo de enlace es cuando el doble de prueba se compila en el módulo de carga, que se ejecuta para validar las pruebas. Este enfoque se utiliza normalmente cuando se ejecuta en un entorno distinto al entorno de destino que requiere dobles para el código de nivel de hardware para la compilación. La alternativa a la sustitución de enlazadores 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 generalmente se realiza mediante la reasignación de punteros de funciones conocidas o el reemplazo de objetos.

Los dobles de prueba son de diferentes tipos y complejidades variables:

Un corolario de dicha inyección de dependencia es que el proceso TDD en sí nunca prueba la base de datos real u otro código de acceso externo. Para evitar errores que puedan surgir de esto, se necesitan otras pruebas que instancian el código basado en pruebas con las implementaciones "reales" de las interfaces discutidas anteriormente. Estas son pruebas de integración y están bastante separadas de las pruebas unitarias de TDD. Hay menos y deben ejecutarse con menos frecuencia que las pruebas unitarias. No obstante, se pueden implementar utilizando el mismo marco de prueba.

Las pruebas de integración que alteran cualquier almacén o base de datos 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 suele lograrse utilizando alguna combinación de las siguientes técnicas:

Mantenga la unidad pequeña

Para TDD, una unidad se define más comúnmente como una clase o un grupo de funciones relacionadas a menudo llamado módulo. Se afirma que mantener las unidades relativamente pequeñas proporciona beneficios críticos, que incluyen:

Las prácticas avanzadas de desarrollo basado en pruebas pueden conducir al desarrollo basado en pruebas de aceptación (ATDD) 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). [15] 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.

Mejores prácticas

Estructura de prueba

El diseño eficaz de un caso de prueba garantiza que se completen todas las acciones requeridas, mejora la legibilidad del caso de prueba y suaviza el flujo de ejecución. Una estructura coherente ayuda a crear un caso de prueba autodocumentado. Una estructura comúnmente aplicada para casos de prueba tiene (1) configuración, (2) ejecución, (3) validación y (4) limpieza.

Mejores prácticas individuales

Algunas de las mejores prácticas que un individuo podría seguir serían separar la lógica común de configuración y desmontaje en los servicios de soporte de prueba utilizados por los casos de prueba apropiados, mantener cada oráculo de prueba enfocado solo en los resultados necesarios para validar su prueba y diseñar pruebas relacionadas con el tiempo para permitir la tolerancia para la ejecución en sistemas operativos en tiempo no real. La práctica común de permitir un margen del 5 al 10 por ciento para la ejecución tardía reduce el número potencial de falsos negativos en la ejecución de la prueba. 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 los casos positivos como para los negativos, durar mucho tiempo y ser legible y mantenible. Los equipos pueden reunirse y revisar pruebas y prácticas de prueba para compartir técnicas efectivas y detectar malos hábitos. [dieciséis]

Prácticas a evitar o “antipatrones”

Comparación y demarcación

TDD y ATDD

El desarrollo basado en pruebas está relacionado, pero es diferente, del desarrollo basado en pruebas de aceptación (ATDD). [17] TDD es principalmente una herramienta de desarrollador para ayudar 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 pueden derivarse de pruebas ATDD, ya que las unidades de código implementan una parte de un requisito. Las pruebas ATDD deben ser legibles por el cliente. Las pruebas TDD no necesitan serlo.

TDD y BDD

BDD ( desarrollo impulsado por el comportamiento ) combina prácticas de TDD y ATDD. [18] 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 pueden traducirse en pruebas automatizadas.

Software para TDD

Existen muchos marcos y herramientas de prueba que son útiles en TDD.

Marcos xUnit

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 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. [19]

resultados del grifo

Los marcos de prueba pueden aceptar resultados de pruebas unitarias en el protocolo Test Anything , independiente del lenguaje, creado en 1987.

TDD para sistemas complejos

Ejercer TDD en sistemas grandes y desafiantes requiere una arquitectura modular, componentes bien definidos con interfaces publicadas y sistemas disciplinados en capas que maximicen la independencia de la plataforma. Estas prácticas comprobadas producen una mayor capacidad de prueba y facilitan la aplicación de la automatización de compilación y prueba. [8]

Diseño para la capacidad de prueba

Los sistemas complejos requieren una arquitectura que cumpla con una variedad de requisitos. Un subconjunto clave de estos requisitos incluye soporte para la prueba completa y efectiva del sistema. El 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, donde se construye un conjunto de gráficos de secuencia, cada uno de los cuales se centra en un único escenario de ejecución a nivel de sistema. El modelo de escenario 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 escenario sirve como un rico conjunto 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 enormemente la construcción de pruebas TDD para un sistema complejo. [8]

Gestión de pruebas para equipos grandes.

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 ampliación hace que los beneficios de TDD se acumulen aún 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í mismo, erosionando los beneficios potenciales. Suena 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.

Crear y gestionar la arquitectura del software de prueba dentro de un sistema complejo es tan importante como la arquitectura central del producto. Los conductores de prueba interactúan con la UUT, los dobles de prueba y el marco de prueba unitaria. [8]

Ventajas y desventajas del desarrollo basado en pruebas

Ventajas

Test Driven Development (TDD) es un enfoque de desarrollo de software en el que las pruebas se escriben antes que el código real. Ofrece varias ventajas:

  1. Cobertura integral de pruebas : TDD garantiza que todo el código nuevo esté cubierto por al menos una prueba, lo que genera un software más sólido.
  2. Mayor confianza en el código : los desarrolladores obtienen una mayor confianza en la confiabilidad y funcionalidad del código.
  3. Código bien documentado : el proceso naturalmente da como resultado un código bien documentado, ya que cada prueba aclara el propósito del código que prueba.
  4. Claridad de requisitos : TDD fomenta una comprensión clara de los requisitos antes de comenzar la codificación.
  5. Facilita la integración continua : se integra bien con los procesos de integración continua, lo que permite realizar pruebas y actualizaciones de código frecuentes.
  6. Aumenta la productividad : muchos desarrolladores descubren que TDD aumenta su productividad.
  7. Refuerza el modelo mental del código : TDD ayuda a construir un modelo mental sólido de la estructura y el comportamiento del código.
  8. Énfasis en el diseño y la funcionalidad : fomenta la atención en el diseño, la interfaz y la funcionalidad general del programa.
  9. Reduce la necesidad de depuración : al detectar problemas en las primeras etapas del proceso de desarrollo, TDD reduce la necesidad de una depuración exhaustiva más adelante.
  10. Estabilidad del sistema : las aplicaciones desarrolladas con TDD tienden a ser más estables y menos propensas a sufrir errores. [20]

Desventajas

Sin embargo, TDD no está exento de inconvenientes:

  1. Mayor volumen de código : la implementación de TDD puede dar como resultado una base de código más grande a medida que las pruebas se suman a la cantidad total de código escrito.
  2. Falsa seguridad de las pruebas : una gran cantidad de pruebas aprobadas a veces puede dar una sensación engañosa de seguridad con respecto a la solidez del código.
  3. Gastos generales de mantenimiento : mantener un gran conjunto de pruebas puede agregar gastos generales al proceso de desarrollo.
  4. Procesos de prueba que requieren mucho tiempo : escribir y mantener pruebas puede llevar mucho tiempo.
  5. Configuración del entorno de prueba : TDD requiere configurar y mantener un entorno de prueba adecuado.
  6. Curva de aprendizaje : Se necesita tiempo y esfuerzo para dominar las prácticas de TDD.
  7. Complicación excesiva : un énfasis excesivo en TDD puede generar un código más complejo de lo necesario.
  8. Descuido del diseño general : centrarse demasiado en aprobar las pruebas a veces puede llevar a descuidar el panorama más amplio en el diseño de software.
  9. Mayores costos : el tiempo y los recursos adicionales necesarios para TDD pueden resultar en mayores costos de desarrollo.

Beneficios

Un estudio de 2005 encontró que usar TDD significaba escribir más pruebas y, a su vez, los programadores que escribían más pruebas tendían a ser más productivos. [21] Las hipótesis relacionadas con la calidad del código y una correlación más directa entre TDD y la productividad no fueron concluyentes. [22]

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 a menudo puede ser más productivo que depurar. [23]

El desarrollo basado en pruebas ofrece más que una simple validación de la corrección, sino que también puede impulsar el diseño de un programa. [24] 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). Entonces, 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 afirmaciones matemáticas o ideas preconcebidas.

El desarrollo basado en pruebas ofrece la posibilidad de dar pequeños pasos cuando sea necesario. Permite al programador concentrarse en la tarea en cuestión, ya que el primer objetivo es aprobar la prueba. Inicialmente no se consideran los casos excepcionales ni el manejo de errores, y las pruebas para crear estas circunstancias extrañas se implementan por separado. El desarrollo basado en pruebas garantiza de esta manera que todo el código escrito esté cubierto por al menos una prueba. Esto le da 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. [25] Un gran número de pruebas ayudan a limitar el número 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, evitando 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 larga y tediosa más adelante en el proyecto.

TDD puede generar un código más modularizado, flexible y extensible. Este efecto a menudo se produce porque la metodología requiere que los desarrolladores piensen en el software en términos de pequeñas unidades que pueden escribirse y probarse de forma independiente e integrarse posteriormente. Esto conduce a clases más pequeñas y enfocadas, un acoplamiento más flexible e interfaces más limpias. 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 modo que los módulos se puedan cambiar fácilmente entre versiones simuladas para pruebas unitarias y versiones "reales" para implementación.

Debido a que no se escribe más código del necesario para pasar un caso de prueba fallido, las pruebas automatizadas tienden a cubrir todas las rutas del código. Por ejemplo, para que un desarrollador de TDD agregue una elserama a una declaración existente if, el desarrollador 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 otras funciones.

Madeyski [26] proporcionó evidencia empírica (a través de una serie de experimentos de laboratorio con más de 200 desarrolladores) sobre 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 medio del efecto representa un efecto mediano (pero cercano a grande) según el metanálisis de los experimentos realizados, lo cual es un hallazgo sustancial. Sugiere una mejor modularización (es decir, un diseño más modular), una reutilización y prueba más sencilla de los productos de software desarrollados debido a la práctica de programación TDD. [26] 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), [27] [28] [29] que son indicadores de la minuciosidad y la eficacia de detección de fallas de pruebas unitarias, respectivamente. El tamaño del efecto de TDD sobre la cobertura de sucursales fue mediano y por lo tanto se considera un efecto sustancial. [26] Estos hallazgos han sido confirmados posteriormente por evaluaciones experimentales más pequeñas de TDD. [30] [31] [32] [33]

Beneficios psicológicos para el programador.

  1. Mayor confianza : TDD permite a los programadores realizar cambios o agregar nuevas funciones con confianza. Saber que el código se prueba constantemente reduce el miedo a romper la funcionalidad existente. Esta red de seguridad puede fomentar enfoques más innovadores y creativos para la resolución de problemas.
  2. Reducción del miedo al cambio, reducción del estrés : en el desarrollo tradicional, cambiar el código existente puede resultar abrumador debido al riesgo de introducir errores. TDD, con su completo conjunto de pruebas, reduce este miedo, ya que las pruebas revelarán inmediatamente cualquier problema causado por los cambios. Saber que el código base tiene una red de seguridad de pruebas puede reducir el estrés y la ansiedad asociados con la programación. Los desarrolladores pueden sentirse más relajados y abiertos a experimentar y refactorizar.
  3. Enfoque mejorado : escribir pruebas primero ayuda a los programadores a concentrarse en los requisitos y el diseño antes de escribir el código. Este enfoque puede conducir a una codificación más clara y con más propósito, ya que el desarrollador siempre está consciente del objetivo que intenta lograr.
  4. Sentido de logro y satisfacción laboral : aprobar exámenes puede proporcionar una sensación de logro rápida y regular, lo que eleva la moral. Esto puede resultar especialmente motivador en proyectos a largo plazo en los que el objetivo final puede parecer lejano. La combinación de todos estos factores puede conducir a una mayor satisfacción laboral. Cuando los desarrolladores se sienten seguros, concentrados y parte de un equipo colaborativo, su satisfacción laboral general puede mejorar significativamente.

Limitaciones

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. [34] Ejemplos de estos son interfaces de usuario , programas que trabajan con bases de datos y algunos que dependen de configuraciones de red específicas. TDD anima a los desarrolladores a poner la cantidad mínima de código en dichos módulos y a maximizar la lógica del código de la biblioteca comprobable, utilizando falsificaciones y simulacros para representar el mundo exterior. [35]

El apoyo de la gestión es esencial. Sin que toda la organización crea que el desarrollo basado en pruebas mejorará el producto, la gerencia puede sentir que el tiempo dedicado a escribir pruebas es una pérdida. [36]

Las pruebas unitarias creadas en un entorno de desarrollo basado en pruebas generalmente las crea el desarrollador que escribe el código que se prueba. Por lo tanto, las pruebas pueden compartir puntos ciegos con el código: si, por ejemplo, un desarrollador no se da cuenta de que ciertos parámetros de entrada deben verificarse, 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 tanto, las pruebas pasarán, dando 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 prueba de software adicionales , como pruebas de integración y pruebas de cumplimiento .

Las pruebas se convierten en parte de los gastos generales de mantenimiento de un proyecto. Las pruebas mal escritas, por ejemplo las que incluyen cadenas de error codificadas, son propensas a fallar y su mantenimiento es costoso. Este es especialmente el caso de las pruebas frágiles. [37] Existe el riesgo de que las pruebas que periódicamente generan fallos falsos sean ignoradas, de modo que cuando se produzca un fallo real, es posible que no se detecte. Es posible escribir pruebas para un mantenimiento bajo y sencillo, por ejemplo mediante la reutilización de cadenas de error, y este debería ser un objetivo durante la fase de refactorización del código descrita anteriormente.

Escribir y mantener una cantidad excesiva de pruebas cuesta tiempo. Además, los módulos más flexibles (con pruebas limitadas) podrían aceptar nuevos requisitos sin necesidad de cambiar las pruebas. Por esas razones, realizar pruebas sólo para condiciones extremas, o una pequeña muestra de datos, puede ser más fácil de ajustar que un conjunto de pruebas muy detalladas.

El nivel de cobertura y detalle de las pruebas logrado 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 temprano. Además, si una arquitectura deficiente, un diseño deficiente o una estrategia de prueba deficiente conducen a un cambio tardío que hace que fallen docenas de pruebas existentes, entonces es importante que se arreglen individualmente. Simplemente eliminarlos, deshabilitarlos o modificarlos precipitadamente puede provocar agujeros indetectables en la cobertura de la prueba.

Conferencia

La primera conferencia TDD se llevó a cabo durante julio de 2021. [38] Las conferencias se grabaron en YouTube [39]

Ver también

Referencias

  1. ^ Lee Copeland (diciembre de 2001). "Programación extrema". Mundo de la informática. Archivado desde el original el 5 de junio de 2011 . Consultado el 11 de enero de 2011 .
  2. ^ ab Newkirk, JW y Vorontsov, AA. Desarrollo basado en pruebas en Microsoft .NET , Microsoft Press, 2004.
  3. ^ Feathers, M. Trabajar eficazmente con código heredado, Prentice Hall, 2004
  4. ^ Kent Beck (11 de mayo de 2012). "¿Por qué Kent Beck se refiere al "redescubrimiento" del desarrollo basado en pruebas?" . Consultado el 1 de diciembre de 2014 .
  5. ^ abc Beck, Kent (8 de noviembre de 2002). Desarrollo basado en pruebas con el ejemplo . Vaseem: Addison Wesley. ISBN 978-0-321-14653-3.
  6. ^ Leybourn, E. (2013) Dirigir la organización ágil: un enfoque ajustado para la gestión empresarial . Londres: Publicación de gobernanza de TI: 176-179.
  7. ^ Mohan, Gayathri. "Pruebas de pila completa". www.thinkworks.com . Consultado el 7 de septiembre de 2022 .
  8. ^ abcdefg "Documento técnico sobre TDD eficaz para sistemas integrados complejos" (PDF) . Soluciones Pathfinder. Archivado desde el original (PDF) el 16 de marzo de 2016.
  9. ^ "Desarrollo ágil basado en pruebas". Sherpa ágil. 2010-08-03. Archivado desde el original el 23 de julio de 2012 . Consultado el 14 de agosto de 2012 .
  10. ^ Burton, Ross (12 de noviembre de 2003). "Subvertir la protección de acceso de Java para pruebas unitarias". O'Reilly Media, Inc. Consultado el 12 de agosto de 2009 .
  11. ^ van Rossum, Guido; Varsovia, Barry (5 de julio de 2001). "PEP 8 - Guía de estilo para código Python". Fundación de software Python . Consultado el 6 de mayo de 2012 .
  12. ^ Newkirk, James (7 de junio de 2004). "Prueba de métodos privados/variables miembro: debería hacerlo o no". Corporación Microsoft . Consultado el 12 de agosto de 2009 .
  13. ^ Puesto, Tim (1 de marzo de 2005). "Cómo probar métodos privados y protegidos en .NET". Proyecto de código . Consultado el 12 de agosto de 2009 .
  14. ^ Fowler, Martín (1999). Refactorización: mejora del diseño del código existente. Boston: Addison Wesley Longman, Inc. ISBN 0-201-48567-2.
  15. ^ Koskela, L. "Prueba basada en: TDD y TDD de aceptación para desarrolladores de Java", Publicaciones Manning, 2007
  16. ^ ab Introducción al desarrollo basado en pruebas (TDD) para sistemas complejos en YouTube por Pathfinder Solutions
  17. ^ Desarrollo basado en pruebas de aceptación Lean-Agile: mejor software mediante la colaboración . Boston: Addison Wesley Profesional. 2011.ISBN 978-0321714084.
  18. ^ "BDD". Archivado desde el original el 8 de mayo de 2015 . Consultado el 28 de abril de 2015 .
  19. ^ "Documento técnico sobre TDD eficaz para sistemas integrados complejos". Soluciones Pathfinder. Archivado desde el original el 20 de agosto de 2013 . Consultado el 27 de noviembre de 2012 .
  20. ^ Ventajas y desventajas del desarrollo basado en pruebas - LASOFT
  21. ^ Erdogmo, Hakan; Morisio, Torchiano. "Sobre la eficacia del enfoque de programación basado en la prueba". Actas de IEEE Transactions on Software Engineering, 31(1). Enero de 2005. (NRC 47445). Archivado desde el original el 22 de diciembre de 2014 . Consultado el 14 de enero de 2008 . Descubrimos que los estudiantes que tomaron primero los exámenes, en promedio, escribieron más exámenes y, a su vez, los estudiantes que escribieron más exámenes tendieron a ser más productivos.
  22. ^ Proffitt, Jacob. "¡TDD ha demostrado su eficacia! ¿O no?". Archivado desde el original el 6 de febrero de 2008 . Consultado el 21 de febrero de 2008 . Así que la relación de TDD con la calidad es, en el mejor de los casos, problemática. 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 el número de pruebas, pero esa correlación es en realidad más fuerte en el grupo sin TDD (que tuvo un único valor atípico en comparación con aproximadamente la mitad del grupo con TDD que estaba fuera de la banda del 95%).
  23. ^ Llopis, Noel (20 de febrero de 2005). "Pasando a través del espejo: desarrollo de juegos basado en pruebas (Parte 1)". Juegos desde dentro . Consultado el 1 de noviembre de 2007 . Comparando [TDD] con el enfoque de desarrollo no basado en pruebas, está reemplazando toda la verificación mental y los pasos del depurador con código que verifica que su programa hace exactamente lo que usted pretendía que hiciera.
  24. ^ Mayr, Herwig (2005). Projekt Engineering Ingenieurmässige Softwareentwicklung in Projektgruppen (2., neu bearb. Aufl. ed.). Múnich: Fachbuchverl. Leipzig en Carl-Hanser-Verl. pag. 239.ISBN 978-3446400702.
  25. ^ Müller, Matías M.; Padberg, Frank. "Acerca del retorno de la inversión del desarrollo basado en pruebas" (PDF) . Universität Karlsruhe, Alemania. pag. 6. S2CID  13905442. Archivado desde el original (PDF) el 8 de noviembre de 2017 . Consultado el 14 de junio de 2012 .
  26. ^ abc Madeyski, L. "Desarrollo basado en pruebas: una evaluación empírica de la práctica ágil", Springer, 2010, ISBN 978-3-642-04287-4 , págs. DOI: 978-3-642-04288-1 
  27. ^ El impacto de la programación Test-First en la cobertura de sucursales y el indicador de puntuación de mutación de las pruebas unitarias: un experimento. por L. Madeyski Tecnología de la información y el software 52 (2): 169-184 (2010)
  28. ^ Sobre los efectos de la programación por pares sobre la minuciosidad y la eficacia en la búsqueda de fallas de las pruebas unitarias por L. Madeyski PROFES 2007: 207-221
  29. ^ Impacto de la programación de pares en la minuciosidad y eficacia de la detección de fallas de los conjuntos de pruebas unitarias. por L. Madeyski Proceso de software: mejora y práctica 13(3): 281-295 (2008)
  30. ^ M. Pančur y M. Ciglarič, "Impacto del desarrollo basado en pruebas en la productividad, el código y las pruebas: un experimento controlado", Tecnología de la información y el software, 2011, vol. 53, núm. 6, págs. 557–573, DOI: 10.1016/j.infsof.2011.02.002
  31. ^ D. Fucci, H. Erdogmus, B. Turhan, M. Oivo y N. Juristo, "Una disección del proceso de desarrollo basado en pruebas: ¿realmente importa probar primero o probar al final?", IEEE Transacciones sobre ingeniería de software, 2017, vol. 43, núm. 7, págs. 597–614, DOI: 10.1109/TSE.2016.2616877
  32. ^ A. Tosun, O. Dieste Tubio, D. Fucci, S. Vegas, B. Turhan, H. Erdogmus, A. Santos, M. Oivo, K. Toro, J. Jarvinen y N. Juristo, "Una industria experimento sobre los efectos del desarrollo basado en pruebas en la calidad y productividad externas", Empirical Software Engineering, 2016, vol. 22, págs. 1–43, DOI: 10.1007/s10664-016-9490-0
  33. ^ B. Papis, K. Grochowski, K. Subzda y K. Sijko, "Evaluación experimental de desarrollo basado en pruebas con pasantes que trabajan en un proyecto industrial real", IEEE Transactions on Software Engineering, 2020, DOI: 10.1109/TSE.2020.3027522
  34. ^ "Problemas con TDD". Dalkescientific.com. 2009-12-29 . Consultado el 25 de marzo de 2014 .
  35. ^ Cazador, Andrew (19 de octubre de 2012). "¿Se abusan de las pruebas unitarias?". Simple-talk.com . Consultado el 25 de marzo de 2014 .
  36. ^ Loughran, Steve (6 de noviembre de 2006). "Pruebas" (PDF) . Laboratorios HP . Consultado el 12 de agosto de 2009 .
  37. ^ "Pruebas frágiles".
  38. ^ Bunardzic, Alex. "Primera Conferencia Internacional de Desarrollo Basado en Pruebas (TDD)". Conferencia TDD . Consultado el 20 de julio de 2021 .
  39. ^ Primera conferencia internacional de TDD: sábado 10 de julio de 2021, archivado desde el original el 21 de diciembre de 2021 , consultado el 20 de julio de 2021

enlaces externos