unit-testing - test - pruebas unitarias laravel
¿Qué tan profundas son las pruebas de su unidad? (17)
Todo debe hacerse lo más simple posible, pero no más simple. - A. Einstein
Una de las cosas más incomprendidas sobre TDD es la primera palabra. Prueba. Es por eso que apareció BDD. Porque la gente realmente no entendía que la primera D era la más importante, es decir, Impulsado. Todos tendemos a pensar un poco sobre las Pruebas, y poco a poco sobre la conducción del diseño. Y supongo que esta es una respuesta vaga a su pregunta, pero probablemente debería considerar cómo manejar su código, en lugar de lo que realmente está probando; eso es algo con lo que una herramienta de cobertura puede ayudarlo. El diseño es un problema bastante más grande y más problemático.
Lo que encontré sobre TDD es que lleva tiempo configurar tus pruebas y ser perezoso por naturaleza. Siempre quiero escribir la menor cantidad de código posible. Lo primero que hago es probar que mi constructor haya establecido todas las propiedades, pero ¿es esto exagerado?
Mi pregunta es ¿a qué nivel de granularidad escribes pruebas unitarias?
..y ¿hay un caso de prueba demasiado?
Creo que debes probar todo en tu "núcleo" de tu lógica de negocios. Getter ans Setter también porque podrían aceptar valores negativos o nulos que no querría aceptar. Si tiene tiempo (siempre depende de su jefe) es bueno probar otra lógica comercial y todos los controladores que llaman a estos objetos (pasa de la prueba unitaria a la prueba de integración lentamente).
Cuanto más leo sobre él, más creo que algunas pruebas unitarias son como algunos patrones: un olor de idiomas insuficientes.
Cuando necesite probar si su captador trivial en realidad devuelve el valor correcto, es porque puede entremezclar el nombre getter y el nombre de la variable miembro. Ingrese ''attr_reader: nombre'' de ruby, y esto ya no puede suceder. Simplemente no es posible en Java.
Si su getter se vuelve no trivial, puede agregarle una prueba.
El desarrollo impulsado por prueba significa que usted deja de codificar cuando pasan todas sus pruebas.
Si no tiene una prueba para una propiedad, ¿por qué debería implementarla? Si no prueba / define el comportamiento esperado en el caso de una asignación "ilegal", ¿qué debería hacer la propiedad?
Por lo tanto, estoy totalmente para probar cada comportamiento que una clase debería exhibir. Incluyendo propiedades "primitivas".
Para facilitar estas pruebas, creé un NUnit TestFixture
simple que proporciona puntos de extensión para establecer / obtener el valor y toma listas de valores válidos y no válidos y tiene una prueba única para verificar si la propiedad funciona correctamente. Probar una sola propiedad podría verse así:
[TestFixture]
public class Test_MyObject_SomeProperty : PropertyTest<int>
{
private MyObject obj = null;
public override void SetUp() { obj = new MyObject(); }
public override void TearDown() { obj = null; }
public override int Get() { return obj.SomeProperty; }
public override Set(int value) { obj.SomeProperty = value; }
public override IEnumerable<int> SomeValidValues() { return new List() { 1,3,5,7 }; }
public override IEnumerable<int> SomeInvalidValues() { return new List() { 2,4,6 }; }
}
Usando lambdas y atributos, esto podría incluso escribirse de manera más compacta. Entiendo que MBUnit tiene incluso algún soporte nativo para cosas como esa. El punto es que el código anterior captura la intención de la propiedad.
PD: Probablemente el PropertyTest también debería tener una forma de verificar que otras propiedades en el objeto no hayan cambiado. Hmm ... de vuelta al tablero de dibujo.
En general, comienzo pequeño, con entradas y salidas que sé que deben funcionar. Luego, cuando soluciono los errores, agrego más pruebas para garantizar que las cosas que he corregido se prueban. Es orgánico y funciona bien para mí.
¿Puedes probar demasiado? Probablemente, pero es probable que sea mejor pecar de cauteloso en general, aunque dependerá de qué tan crítica sea su aplicación.
En la mayoría de los casos, yo diría que, si hay una lógica allí, pruébelo. Esto incluye constructores y propiedades, especialmente cuando se establece más de una cosa en la propiedad.
Con respecto a demasiadas pruebas, es discutible. Algunos dirían que todo debería ser probado para ser robusto, otros dicen que para las pruebas eficientes, solo deberían probarse las cosas que podrían romperse (es decir, la lógica).
Me inclinaría más hacia el segundo campamento, solo por experiencia personal, pero si alguien decidiera probarlo todo, no diría que fue demasiado ... quizás un poco exagerado para mí, pero no demasiado para ellos.
Entonces, No, yo diría que no existe una prueba de "demasiado" en el sentido general, solo para individuos.
Escriba las pruebas unitarias para las cosas que espera romper, y para los casos extremos. Después de eso, se deben agregar casos de prueba a medida que ingresen los informes de errores, antes de escribir la corrección para el error. El desarrollador puede estar seguro de que:
- El error está solucionado
- El error no volverá a aparecer.
Según el comentario adjunto, creo que este enfoque para escribir pruebas unitarias podría causar problemas, si se descubren muchos errores, a lo largo del tiempo, en una clase determinada. Probablemente este sea el lugar donde la discreción es útil: agregue pruebas unitarias solo para errores que puedan volver a ocurrir, o donde su repetición podría causar problemas graves. He descubierto que una medida de las pruebas de integración en pruebas unitarias puede ser útil en estos escenarios: el código de prueba en las rutas de códigos superiores puede cubrir los recorridos de codificación más abajo.
Escribo exámenes para cubrir las suposiciones de las clases que escribiré. Las pruebas hacen cumplir los requisitos. Esencialmente, si x nunca puede ser 3, por ejemplo, me aseguraré de que haya una prueba que cubra ese requisito.
Invariablemente, si no escribo una prueba para cubrir una condición, aparecerá más tarde durante la prueba "humana". Ciertamente escribiré uno entonces, pero prefiero atraparlos temprano. Creo que el punto es que las pruebas son tediosas (quizás) pero necesarias. Escribo suficientes pruebas para completar, pero no más que eso.
Esta respuesta es más para averiguar cuántas pruebas unitarias usar para un método dado que usted sabe que desea probar en unidades debido a su importancia crítica / importancia. Usando la técnica de prueba de ruta básica de McCabe, podría hacer lo siguiente para tener una mejor cobertura de código de confianza que la simple "cobertura de declaración" o "cobertura de sucursal":
- Determine el valor de Complejidad Ciclomática de su método que desea probar por unidad (por ejemplo, Visual Studio 2010 Ultimate puede calcularlo con herramientas de análisis estáticas, de lo contrario, puede calcularlo manualmente mediante el método de flujografía - http://users.csc.calpoly.edu/~jdalbey/206/Lectures/BasisPathTutorial/index.html )
- Enumere el conjunto básico de caminos independientes que fluyen a través de su método - vea el enlace de arriba para ver el ejemplo del diagrama de flujo
- Prepare pruebas unitarias para cada camino de base independiente determinado en el paso 2
La respuesta clásica es "probar cualquier cosa que pueda romperse". Lo interpreto en el sentido de que probar setters y getters que no hacen nada más que establecer u obtener es probablemente demasiadas pruebas, sin necesidad de tomarse el tiempo. A menos que su IDE escriba esos para usted, entonces también podría.
Si su constructor no configura las propiedades podría ocasionar errores más adelante, entonces probar que están configuradas no es exagerado.
Me pagan por un código que funciona, no para pruebas, así que mi filosofía es probar lo menos posible para alcanzar un determinado nivel de confianza (sospecho que este nivel de confianza es alto en comparación con los estándares de la industria, pero eso podría ser una arrogancia). . Si normalmente no cometo un error (como establecer las variables incorrectas en un constructor), no lo pruebo. Tiendo a dar sentido a los errores de prueba, así que soy extremadamente cuidadoso cuando tengo lógica con condicionales complicados. Cuando codifico en un equipo, modifico mi estrategia para probar cuidadosamente el código que, colectivamente, nos equivocamos.
Diferentes personas tendrán diferentes estrategias de evaluación basadas en esta filosofía, pero eso me parece razonable dado el inmaduro estado de comprensión de cómo las pruebas pueden encajar mejor en el ciclo interno de la codificación. Diez o veinte años a partir de ahora tendremos una teoría más universal de qué pruebas escribir, qué pruebas no escribir y cómo diferenciarlas. Mientras tanto, la experimentación parece estar en orden.
No hago pruebas unitarias simples de métodos setter / getter que no tienen efectos secundarios. Pero hago una prueba unitaria de cada otro método público. Intento crear pruebas para todas las condiciones de contorno en mis algoritmos y verificar la cobertura de mis pruebas unitarias.
Es mucho trabajo, pero creo que vale la pena. Prefiero escribir código (incluso código de prueba) que pasar el código en un depurador. Encuentro que el ciclo code-build-deploy-debug consume mucho tiempo y cuanto más exhaustivas sean las pruebas unitarias que he integrado en mi compilación, menos tiempo pasaré pasando por ese ciclo code-build-deploy-debug.
Usted no dijo por qué la arquitectura también está codificando. Pero para Java utilizo Maven 2 , JUnit , DbUnit , Cobertura y EasyMock .
Parte del problema de omitir pruebas simples ahora es que en el futuro la refactorización podría hacer que esa propiedad simple sea muy complicada con mucha lógica. Creo que la mejor idea es que puede usar las pruebas para verificar los requisitos del módulo. Si al pasar X debes recuperar Y, entonces eso es lo que quieres probar. Luego, cuando cambie el código más adelante, puede verificar que X le otorgue Y, y puede agregar una prueba para que A le proporcione B, cuando ese requisito se agregue más adelante.
Descubrí que el tiempo que gasto durante las pruebas de escritura de desarrollo inicial vale la pena en la primera o segunda corrección de errores. La capacidad de recoger un código que no ha mirado en 3 meses y estar razonablemente seguro de que su solución cubre todos los casos, y "probablemente" no infringe nada, es muy valioso. También encontrará que las pruebas unitarias ayudarán a los errores de clasificación mucho más allá de la traza de la pila, etc. Ver cómo las piezas individuales de la aplicación funcionan y fallan proporciona una gran idea de por qué funcionan o fallan en conjunto.
Por lo tanto, cuanto más manejo mi programación escribiendo pruebas, menos me preocupa el nivel de granalidad de las pruebas. Mirando hacia atrás, parece que estoy haciendo lo más simple posible para lograr mi objetivo de validar el comportamiento . Esto significa que estoy generando una capa de confianza de que mi código está haciendo lo que solicité, sin embargo, esto no se considera una garantía absoluta de que mi código esté libre de errores. Siento que el equilibrio correcto es probar el comportamiento estándar y tal vez una o dos cajas de borde luego pasar a la siguiente parte de mi diseño.
Acepto que esto no cubrirá todos los errores y utilizará otros métodos de prueba tradicionales para capturarlos.
Pruebe el código fuente que le preocupa.
No es útil para probar partes del código en las que tiene mucha confianza, siempre y cuando no cometa errores.
Pruebe las correcciones de errores, de modo que sea la primera y la última vez que corrija un error.
Prueba para obtener la confianza de porciones de código ocultas, para que crees conocimiento.
Pruebe antes de refactorizar pesado y mediano, para que no rompa las características existentes.
Realizo una prueba unitaria para alcanzar la cobertura máxima factible. Si no puedo acceder a algún código, me refactorizo hasta que la cobertura sea lo más completa posible
Después de terminar la prueba de escritura deslumbrante, suelo escribir un caso de prueba que reproduce cada error
Estoy acostumbrado a separar entre pruebas de código y pruebas de integración. Durante las pruebas de integración, (que también son pruebas unitarias, pero en grupos de componentes, por lo que no es exactamente para qué sirven las pruebas unitarias) Voy a probar que los requisitos se implementen correctamente.
Para aquellos que proponen probar "todo": se dan cuenta de que "probar completamente" un método como int square(int x)
requiere unos 4 mil millones de casos de prueba en los lenguajes comunes y entornos típicos.
De hecho, es incluso peor que eso: un método void setX(int newX)
también está obligado a no alterar los valores de ningún otro miembro además de x
- ¿está probando que obj.y
, obj.z
, etc. permanecen sin cambios? después de llamar a obj.setX(42);
?
Es solo práctico probar un subconjunto de "todo". Una vez que aceptas esto, se vuelve más apetecible considerar no probar un comportamiento increíblemente básico. Cada programador tiene una distribución de probabilidad de ubicaciones de errores; el enfoque inteligente es enfocar su energía en probar regiones donde estima que la probabilidad de error es alta.