Nuestro colega promueve las pruebas unitarias de escritura como algo que realmente nos ayuda a refinar nuestro diseño y refactorizar las cosas, pero no veo cómo. Si estoy cargando un archivo CSV y lo analizo, ¿cómo me ayudará la prueba unitaria (validando los valores en los campos) a verificar mi diseño? Mencionó el acoplamiento y la modularidad, etc., pero para mí no tiene mucho sentido, aunque no tengo muchos antecedentes teóricos.
No es lo mismo que la pregunta que marcó como duplicado, me interesarían ejemplos reales de cómo esto ayuda, no solo la teoría que dice "ayuda". Me gusta la respuesta a continuación y el comentario, pero me gustaría obtener más información.
design
unit-testing
object-oriented-design
Usuario039402
fuente
fuente
Respuestas:
Las pruebas unitarias no solo facilitan el diseño, sino que ese es uno de sus principales beneficios.
Escribir prueba primero elimina la modularidad y la estructura de código limpia.
Cuando escriba su prueba de código primero, encontrará que cualquier "condición" de una unidad de código dada se expulsa naturalmente a las dependencias (generalmente a través de simulaciones o apéndices) cuando las asume en su código.
"Dada la condición x, espere comportamiento y", a menudo se convertirá en un trozo para suministrar
x
(que es un escenario en el que la prueba necesita verificar el comportamiento del componente actual) yy
se convertirá en un simulacro, una llamada a la que se verificará en el final de la prueba (a menos que sea un "debería regresary
", en cuyo caso la prueba solo verificará el valor de retorno explícitamente).Luego, una vez que esta unidad se comporta como se especifica, pasa a escribir las dependencias (para
x
yy
) que ha descubierto.Esto hace que escribir código limpio y modular sea un proceso muy fácil y natural, donde de lo contrario a menudo es fácil difuminar las responsabilidades y unir los comportamientos sin darse cuenta.
Escribir pruebas más adelante le dirá cuándo su código está mal estructurado.
Cuando escribir pruebas para un fragmento de código se vuelve difícil porque hay demasiadas cosas para tropezar o burlarse, o porque las cosas están demasiado unidas, sabes que tienes mejoras que hacer en tu código.
Cuando "cambiar las pruebas" se convierte en una carga porque hay tantos comportamientos en una sola unidad, sabe que tiene que hacer mejoras en su código (o simplemente en su enfoque para escribir pruebas, pero este no es el caso en mi experiencia) .
Cuando sus escenarios se vuelven demasiado complicados ("if
x
andy
andz
then ...") porque necesita abstraer más, sabe que tiene que hacer mejoras en su código.Cuando terminas con las mismas pruebas en dos dispositivos diferentes debido a la duplicación y la redundancia, sabes que tienes mejoras que hacer en tu código.
Aquí hay una excelente charla de Michael Feathers que demuestra la estrecha relación entre la capacidad de prueba y el diseño en el código (originalmente publicado por displayName en los comentarios). La charla también aborda algunas quejas comunes y conceptos erróneos sobre el buen diseño y la capacidad de prueba en general.
fuente
Lo mejor de las pruebas unitarias es que le permiten usar su código de la misma manera que otros programadores usarán su código.
Si su código es incómodo para la prueba unitaria, entonces probablemente será incómodo usarlo. Si no puede inyectar dependencias sin saltar a través de aros, entonces su código probablemente será inflexible de usar. Y si necesita pasar mucho tiempo configurando datos o averiguar en qué orden hacer las cosas, su código bajo prueba probablemente tenga demasiado acoplamiento y será difícil trabajar con él.
fuente
Me tomó bastante tiempo darme cuenta, pero el beneficio real (editar: para mí, su kilometraje puede variar) de hacer un desarrollo impulsado por pruebas ( usando pruebas unitarias) es que tiene que hacer el diseño API por adelantado .
Un enfoque típico para el desarrollo es descubrir primero cómo resolver un problema determinado y, con ese conocimiento y el diseño de implementación inicial, de alguna manera invocar su solución. Esto puede dar algunos resultados bastante interesantes.
Al hacer TDD, primero debe escribir el código que usará su solución. Parámetros de entrada y salida esperada para que pueda asegurarse de que sea correcto Eso a su vez requiere que descubras lo que realmente necesitas que haga, para que puedas crear pruebas significativas. Entonces y solo entonces implementas la solución. También es mi experiencia que cuando sabes exactamente lo que se supone que debe lograr tu código, se vuelve más claro.
Luego, después de la implementación, las pruebas unitarias lo ayudan a garantizar que la refactorización no rompa la funcionalidad, y brindan documentación sobre cómo usar su código (¡lo cual sabe que es correcto cuando pasó la prueba!). Pero estos son secundarios: el mayor beneficio es la mentalidad en la creación del código en primer lugar.
fuente
Estoy de acuerdo al 100% en que las pruebas unitarias ayudan a "ayudarnos a refinar nuestro diseño y refactorizar las cosas".
Tengo dos opiniones sobre si te ayudan a hacer el diseño inicial . Sí, revelan defectos obvios y te obligan a pensar en "¿cómo puedo hacer que el código sea verificable"? Esto debería conducir a menos efectos secundarios, configuración y configuraciones más fáciles, etc.
Sin embargo, en mi experiencia, las pruebas unitarias excesivamente simplistas, escritas antes de que realmente comprenda cuál debería ser el diseño, (es cierto que es una exageración de la TDD de núcleo duro, pero con demasiada frecuencia los codificadores escriben una prueba antes de pensar demasiado) a menudo conducen a la anemia. modelos de dominio que exponen demasiados elementos internos.
Mi experiencia con TDD fue hace varios años, por lo que estoy interesado en saber qué técnicas más nuevas podrían ayudar a escribir pruebas que no sesguen demasiado el diseño subyacente. Gracias.
fuente
La prueba de unidad le permite ver cómo funcionan las interfaces entre funciones y, a menudo, le da una idea de cómo mejorar tanto el diseño local como el diseño general. Además, si desarrolla sus pruebas unitarias mientras desarrolla su código, tiene un conjunto de pruebas de regresión listo. No importa si está desarrollando una interfaz de usuario o una biblioteca de fondo.
Una vez que se desarrolla el programa (con pruebas unitarias), a medida que se descubren errores, puede agregar pruebas para confirmar que los errores se corrigieron.
Yo uso TDD para algunos de mis proyectos. Puse un gran esfuerzo en la elaboración de ejemplos que extraigo de libros de texto o de documentos que se consideran correctos, y pruebo el código que estoy desarrollando utilizando estos ejemplos. Cualquier malentendido que tenga sobre los métodos se hace muy evidente.
Tiendo a ser un poco más flexible que algunos de mis colegas, ya que no me importa si el código se escribe primero o si la prueba se escribe primero.
fuente
Cuando desee realizar una prueba unitaria de su analizador sintáctico detectando el valor delimitado correctamente, puede pasar una línea desde un archivo CSV. Para hacer que su prueba sea directa y corta, puede probarla a través de un método que acepte una línea.
Esto hará que separe automáticamente la lectura de líneas de la lectura de valores individuales.
En otro nivel, es posible que no desee colocar todo tipo de archivos CSV físicos en su proyecto de prueba, pero haga algo más legible, simplemente declarando una gran cadena CSV dentro de su prueba para mejorar la legibilidad y la intención de la prueba. Esto lo llevará a desacoplar su analizador de cualquier E / S que haría en otro lugar.
Solo un ejemplo básico, solo comienza a practicarlo, sentirás la magia en algún momento (lo he hecho).
fuente
En pocas palabras, escribir pruebas unitarias ayuda a exponer fallas en su código.
Esta espectacular guía para escribir código comprobable , escrita por Jonathan Wolter, Russ Ruffer y Miško Hevery, contiene numerosos ejemplos de cómo los defectos en el código, que inhiben las pruebas, también evitan la reutilización fácil y la flexibilidad del mismo código. Por lo tanto, si su código es comprobable, es más fácil de usar. La mayoría de las "morales" son consejos ridículamente simples que mejoran enormemente el diseño del código ( Dependency Injection FTW).
Por ejemplo: es muy difícil probar si el método computeStuff funciona correctamente cuando el caché comienza a expulsar cosas. Esto se debe a que debe agregar manualmente basura al caché hasta que el "bigCache" esté casi lleno.
Sin embargo, cuando usamos la inyección de dependencias, es mucho más fácil probar si el método computeStuff funciona correctamente cuando el caché comienza a expulsar cosas. Todo lo que hacemos es crear una prueba en la que llamamos
new HereIUseDI(buildSmallCache());
Aviso, tenemos un control más matizado del objeto y paga dividendos de inmediato.Se pueden obtener beneficios similares cuando nuestro código requiere datos que generalmente se encuentran en una base de datos ... simplemente pase EXACTAMENTE los datos que necesita.
fuente
Dependiendo de lo que se entiende por 'Pruebas de unidad', no creo que las pruebas de Unidad de bajo nivel faciliten un buen diseño tanto como las pruebas de integración de nivel ligeramente superior , pruebas que prueban que un grupo de actores (clases, funciones, lo que sea) en su código se combina correctamente para producir una gran cantidad de comportamientos deseables que se han acordado entre el equipo de desarrollo y el propietario del producto.
Si puede escribir pruebas en esos niveles, lo empuja hacia la creación de un código agradable, lógico, similar a la API que no requiere muchas dependencias locas; el deseo de tener una configuración de prueba simple lo conducirá naturalmente a no tener muchas dependencias locas o código estrechamente acoplado.
Sin embargo, no se equivoque: las pruebas unitarias pueden llevarlo a un mal diseño, así como a un buen diseño. He visto a los desarrolladores tomar un poco de código que ya tiene un diseño lógico agradable y una sola preocupación, y lo separan e introducen más interfaces con el único propósito de probar, y como resultado hacen que el código sea menos legible y más difícil de cambiar , así como posiblemente incluso tener más errores si el desarrollador ha decidido que tener muchas pruebas unitarias de bajo nivel significa que no tienen que tener pruebas de alto nivel. Un ejemplo favorito en particular es un error que arreglé donde había un montón de código 'comprobable' muy desglosado relacionado con la obtención de información dentro y fuera del portapapeles. Todo desglosado y desacoplado a niveles muy pequeños de detalle, con muchas interfaces, muchas simulaciones en las pruebas y otras cosas divertidas. Solo un problema: no había ningún código que realmente interactuara con el mecanismo del portapapeles del sistema operativo,
Las pruebas unitarias definitivamente pueden impulsar su diseño, pero no lo guían automáticamente a un buen diseño. Es necesario tener ideas sobre qué buen diseño es que vaya más allá de "este código se prueba, por lo tanto, es comprobable, por lo tanto es bueno".
Por supuesto, si usted es una de esas personas para quienes 'pruebas unitarias' significa 'cualquier prueba automatizada que no se maneja a través de la interfaz de usuario', entonces algunas de esas advertencias pueden no ser tan relevantes, como dije, creo que esas son más altas Las pruebas de integración de nivel son a menudo las más útiles cuando se trata de impulsar su diseño.
fuente
Las pruebas unitarias pueden ayudar con la refactorización cuando el nuevo código pasa todos los las antiguas pruebas.
Supongamos que ha implementado un bubbleort porque tenía prisa y no le preocupaba el rendimiento, pero ahora desea un quicksort porque los datos se están alargando. Si todas las pruebas pasan, las cosas se ven bien.
Por supuesto, las pruebas deben ser exhaustivas para que esto funcione. En mi ejemplo, es posible que sus pruebas no cubran la estabilidad porque eso no fue una preocupación con la clasificación de burbujas.
fuente
He encontrado que las pruebas unitarias son las más valiosas para facilitar el mantenimiento a largo plazo de un proyecto. Cuando vuelvo a un proyecto después de meses y no recuerdo muchos de los detalles, ejecutar pruebas me impide romper cosas.
fuente