2011-11-08 7 views
10

Esto es más una cuestión de diseño.¿Debería cada derivación posible en un método tener un junit separado?

Suponga que tiene un método como este (como ejemplo):

if (x == 5) { 
    c = 1; 
} else { 
if (z != 2) { 
    b = 6; 
} else { 
    a = 3; 
} 

¿Cree usted que es la mejor práctica para tener un JUnit para cada rama posible? Es decir, testx5, xnot5znot2 prueba, testxnot5z2, etc, o algo así:

void testMethod() { 
// x is 5 
test/assert code; 

// x not 5, z not 2 
test/assert code; 

// x not 5, z is 2 
test/assert code 

// etc 
} 

EDIT: Para ser claro, mi objetivo es la cobertura de código completa. Solo quiero saber si debo hacer una nueva prueba para cada rama o combinarla en una prueba. Gracias por su aporte.

Respuesta

9

El JUnit FAQ parece indicar que es mejor tener más pruebas con menos aserciones, aunque solo sea porque JUnit solo informará la primera falla de aserción en un método de prueba.Con un método, si rompiste el caso x = 5, no tendrías forma de saber si alguno de los x! = 5 casos todavía funcionaba.

+0

Aceptado para comprender el problema central sobre el que me estaba preguntando y el enlace a las preguntas frecuentes de JUnit. – AHungerArtist

4

En las pruebas unitarias, su objetivo es probar los comportamientos, no el "código". Piense en el método que está probando una caja negra y desea ver si funciona correctamente. No sabe cómo funciona internamente, pero espera ciertos resultados para ciertas entradas. Por lo tanto, querrá crear pruebas para diferentes casos de cómo esperaría que el código funcionara como si no supiera cómo funciona realmente el funcionamiento interno del método. Entonces, escribiría pruebas como "applysDiscountToShoppingCart" y "addsDeliveryFeeToShoppingCart".

Ahora, dicho todo esto, también es útil crear "casos extremos" en los que está probando cosas que probablemente se romperán (como nulos, ceros, negativos, datos demasiado grandes/pequeños, etc.) para ver si falla de una manera esperada también. Por lo general, para escribirlos, necesitas saber cómo funciona realmente el método. Si puede diseñar pruebas que cubran todo su código, ¡eso es genial! La cobertura de prueba del 100% es algo definitivo que hay que esforzarse, pero no siempre es práctico (o útil) según la situación.

+1

Pregunta si debería escribir métodos de prueba separados para diferentes entradas/casos de uso de un método, o simplemente colocar múltiples aserciones en un método de prueba. Esto realmente no responde a esa pregunta, a pesar de que es buena información por sí misma. –

+1

Ese es un buen punto, creo que acabo de leer su pregunta de forma un poco diferente. En última instancia, las ramas suelen dictar diferentes comportamientos para diferentes condiciones, por lo que la respuesta sería aún múltiples métodos de prueba con la prueba de un comportamiento por método de prueba. – Todd

+1

Esa parece ser la sabiduría convencional. Pero también he tenido casos en los que preferí poner múltiples afirmaciones en un método de prueba porque de lo contrario la clase de prueba se hincharía con poca ganancia. Como cuando se prueban los métodos 'hashCode' o' equals'. Normalmente siempre probarías las mismas cosas, como la transitividad y la reflexividad, así que realmente no me importa si solo estoy viendo el primer error. Una afirmación errónea significa que el método es incorrecto, así que lo arreglaría antes que el resto, lo que podría depender de esa afirmación de todos modos. Escribir pruebas es una molestia, es comprensible que alguien quiera hacerlo rápido. –

3

Especialmente en los servidores de compilación es más fácil tener muchos testcases/funciones diferentes porque será fácil identificar qué prueba falla. Otro inconveniente es que el caso de prueba se detendrá si el primero falla, y no sabrá el resultado de los otros casos de prueba.

Para mí, personalmente, este beneficio se detiene cuando tienes que hacer una gran cantidad de copiado para configurar/explicar el caso de prueba, en ese caso, haré varias afirmaciones en el mismo caso de prueba.

+0

Si está haciendo mucho copy-pasta para su configuración, ¿no debería usar un método 'setUp()' para su clase de prueba? Sin mencionar, si la primera afirmación falla, entonces no se sabe si la otra afirma * también * tiene problemas. – Kane

+0

No estoy hablando de la función setUp() en jUnit, sino que hablo de la parte donde el caso de prueba describe la situación que se prueba (crea burlas, etc.). Actualizaré la respuesta para reflejar su buen punto de que no verá si el otro afirma no funciona o no. – Thirler

9

Lo que está debatiendo se llama Sucursal Cobertura.

La sabiduría convencional es que si es lo suficientemente importante como para escribir código para cubrir ese caso de uso, es lo suficientemente importante como para escribir un caso de prueba para cubrir ese código. Así que esto parece indicar que una cobertura de 100% de sucursales es un objetivo excelente (y también implicará una cobertura del 100% del estado de cuenta, pero no necesariamente una cobertura del 100% del bucle o del 100% de la condición).

Sin embargo, también necesita equilibrar el esfuerzo de escribir pruebas con el valor de obtener esas pruebas. Por ejemplo, si el código que está probando tiene un try/catch para atrapar una excepción marcada, pero la excepción casi nunca se lanza (o es difícil hacer que se arroje en un caso de prueba), luego se escribe una prueba para cubrir esa excepción probablemente no valga la pena su tiempo.

Esta es la razón por la que ve en muchos lugares que el objetivo de un determinado% de cobertura de prueba es una mala idea, porque termina escribiendo casos de prueba para obtener cobertura, no para encontrar errores. En su ejemplo, sí, cada rama merece su propio caso de prueba. En cada pieza de código de producción, probablemente no sea necesario.

Cuestiones relacionadas