2008-12-12 13 views
43

Cuál es la mejor manera de probar la unidad un método que pone en múltiples métodos, por ejemplo:Unidad de evaluación de un método que llama a otro método

modify(string value) 
{ 
    if(value.Length > 5) replaceit(value); 

    else changeit(value); 
} 

Esta pseudo código tiene un método de modificación que (actualmente) llama a cualquiera replaceit() o changeit(). Ya he escrito pruebas para replaceit y changeit, por lo que escribir una nueva prueba para modificar será del 99% el mismo conjunto de código. Necesito probarlo porque puede cambiar en el futuro.

¿Copio y pego el código de prueba existente? Mueva el código de prueba a una función común? ¿Alguna otra idea? No estoy seguro de la mejor práctica aquí.

Respuesta

28

Este es un escenario clásico de prueba basado en el comportamiento frente a basado en el comportamiento.

En este ejemplo ridículamente simple probar la salida está bien. En algún momento, sin embargo, se encontrará con pruebas en las que inspeccionar el estado después de la ejecución es complicado. En su lugar, desea verificar el comportamiento (por ejemplo, verificar que se haya llamado a changeit con un valor específico).

En ese momento, es probable que deba considerar un marco de objeto falso como Rhino.Mocks (.Net) o Mockito (Java) y comenzar a escribir más código basado en la interfaz.

13

Si ya ha probado replaceit() y changeit() de forma independiente, entonces lo único que le queda por probar es la condición if. Pruebe modify() con algunos valores para asegurarse de que llama a la función correcta en las condiciones adecuadas (esas condiciones son null y Strings de longitud 4, 5 y 6 para el código de ejemplo que proporcionó).

2

Bueno, no, su código de prueba no va a ser el 99% igual, porque en realidad está probando algo diferente aquí, a menos que replaceit, changeit y modify all devuelvan los mismos valores.

No estoy seguro de por qué la dificultad. La prueba para el método de modificación debe tener aproximadamente cuatro líneas de longitud. Como ya está probando la funcionalidad subyacente y todo lo que quiere hacer es asegurarse de que este método en particular no se rompa, escribiendo una prueba que pruebe las dos posibles rutas de código en esta función, los valores de retorno esperados deberían ser suficientes.

3

¿Qué es "el código de prueba" en este caso? Configurar y verificar los resultados? Si es así, lo refactorizaría en un método diferente y lo usaría de cada una de las pruebas. Solo haría esto si hubiera una cantidad significativa de esto: hay un beneficio de legibilidad al poder ver todo lo que hace una prueba, simplemente leyendo el código de ese método.

Los métodos de prueba complicados a menudo me molestan para empezar, para ser honesto, a menudo no se pueden evitar de manera realista, pero si puede simplificarlos, vale la pena hacerlo.

+0

No estoy de acuerdo con la descomposición en factores de las pruebas en funciones auxiliares. Creo que el código de prueba es mucho más legible/mantenible cuando todas las afirmaciones permanecen en las funciones de prueba. Cuando las personas que escriben factores afirman, tienden a comenzar "pruebas de escopeta" y dejan de pensar en sus casos reales de prueba. –

+1

@Scotty: depende. Algunas pruebas son inevitablemente largas para configurar y verificar. Podemos tratar de evitarlo todo lo que queramos, pero a veces solo es necesario. Los métodos de ayuda pueden ahorrar una gran cantidad de código repetitivo. –

2

Si ya escribió las pruebas para replaceit() y changeit(), la prueba de modificación simplemente verificará que se devuelvan resultados diferentes según el valor de 'valor'. Sin embargo, simplemente volverás a aplicar la lógica del método en la prueba, lo cual es un poco absurdo.

En este caso, no probaría la modificación hasta que tenga una lógica más compleja, o mejor, es utilizada por otro método que es más significativo de probar.

5

Solo prueba modify.

Se supone que Modify devuelve ciertos valores cuando se le dan ciertos valores.

Es poco importante cómo Modificar hace su trabajo - sólo que hace su trabajo.

Y si, en el futuro, cambia modify para utilizar métodos diferentes (o ningún método), no afecta, no debe, y no afectará sus pruebas.

Dicho esto, también prueba replaceit' and changeit`.

2

Básicamente necesita 2 pruebas.

1) Pase una cadena como "The Quick Brown Fox Jumps!" (Longitud superior a cinco) se asegura de que el valor se ve afectado por replaceit(...)

2) Pass en una cadena como "Foo" (longitud es inferior a cinco) y asegúrese de que el valor se ve afectado por changeit(...)

su prueba (en pseudo código) podría tener este aspecto:

testLongValue() { 
    string testValue = "A value longer than 5 chars"; 
    string expected = "Replaced!"; 
    string actual = modify(testValue); 
    assertEqual(expected, actual); 
} 

testShortValue() { 
    string testValue = "len4"; 
    string expected = "Changed!"; 
    string actual = modify(testValue); 
    assertEqual(expected, actual); 
} 

Obviamente te podría dar un ejemplo más realista si sabía lo que replacit() y changeit() se supone que debe hacer, pero esto debe darle la idea. Si muta la referencia del valor original en lugar de devolverlo, puede usar testValue como el valor real después de que se produce la llamada.

+0

Así es como yo lo haría también. Ya que ha probado replaceit() y changeit() completamente solo, solo necesita probar la lógica que está contenida en modify(). Un caso de prueba que active replaceit(), y uno que pruebe changeit(), ambos verificando que hicieron lo que se esperaba, debería estar bien. –

+0

Agregaría el caso límite donde la longitud es exactamente 5 – Kena

+1

También agregaría una prueba para nulo, solo como un recordatorio para mí mismo para agregar una cláusula de guardia. –

2

Al probar las condiciones de contorno como if (value.length > 5) usted debe asegurarse de que sus datos de prueba contiene valores de value que tienen longitud 4, 5 o 6.

0

Igual que Justin Estándar, más pasar null como valor (que, obviamente, falla por el fragmento de código que nos da;)) regla básica para la Prueba de la unidad es "prueba sólo lo que es específico para el método bajo prueba" . Y es bastante ... raro tener un método que no llame a otro.

12

Tiene una serie de opciones. Cuál es el mejor depende de detalles que no están claros en su pregunta.

  • prueba modify como si fuera un método no relacionado. Ventaja: en algún momento podría convertirse en uno.
  • simplemente prueba que tienes el enunciado if correcto. Es decir, pruebe lo suficiente para que las pruebas lo obliguen a escribir la implementación que necesita (cuando llamar a replaceit y changeit es simplemente la implementación más simple que podría funcionar. Si está practicando TDD, esto debería ser algo natural para usted. cobertura de prueba alta sin mucho esfuerzo duplicado
  • Método de subclase y sustitución (esta es una técnica de interrupción de dependencia del libro "Trabajar eficazmente con código heredado"): pruebe el método en una subclase que introduzca únicamente con fines de prueba, que anula replaceit y changeit con respuestas enlatadas o para que establezcan variables de detección (variables que indican si el método ha sido llamado con los valores correctos). Ventaja: podría ser posible y simplifique sus pruebas (o no), a veces incluso haga las pruebas posibles.
  • Extraiga una nueva clase para los métodos replaceit y changeit, incluida una interfaz para esa clase. Stub o Mock esa interfaz cuando se prueba modify. Ventaja: ambos pueden hacer que su diseño sea más comprobable y mejor desacoplados/reutilizables en general (o no).
+0

+1 Opción 2 - Opción 3 - Opción 4 en ese orden de preferencia. – Gishu

4

En orden de preferencia

  1. modificar (prueba) solo tiene 2 escenarios (cada brazo del caso prop), por lo que me gustaría escribir 2 pruebas para Modificar de la forma.
    Si el resultado esperado de replaceit (valor) es fácil determinar ..

.

public TestModifyIfValueLength..() 
    { 
     string expectedValue = .. ;// literal result of replaceit(value) 
     Assert.Equals(expectedValue, modify("asd")); 
    } 
  1. Si no es así, considere el uso de un talón (subclase uso y anular changeit, replaceit) para verificar que el método correcto se llama.
  2. Si el talón es demasiado trabajo, haga lo de Simulacro. Extraiga una interfaz y configure las expectativas en changeit, replaceit.

Supuestos

  • Usted tienen pruebas para replaceit (valor) y changeit (valor), que ponen a prueba (por ejemplo, todas las condiciones de contorno para) los 2 métodos integral.
  • replaceit() y changeit() son métodos públicos. De lo contrario, debería considerar escribir pruebas solo en los métodos públicos. Debes tener la libertad de modificar/eliminar métodos privados sin que el código de prueba lo sepa.
2

Puede crear un func de los métodos y burlarse de esos funcs. O bien, puede crear un método virtual y usar el simulacro de Rhino: simulacro parcial, puede simular esos métodos virtuales.

Cuestiones relacionadas