2010-04-26 13 views
8

estaba probando una clase String multiplicador con un método multiply() que tiene 2 números como entradas (como String) y devuelve el número del resultado (como String)básico jUnit Preguntas

public String multiply(String num1, String num2);  

he hecho la puesta en práctica y ha creado una clase de prueba con los casos de prueba siguientes que implican el parámetro de cadena de entrada como

  1. números válidos
  2. caracteres
  3. símbolo especial
  4. cadena vacía
  5. valor nulo
  6. número negativo
  7. flotador
  8. límite valora
  9. números que son válidos, pero su producto está fuera de rango
  10. números will + firman (+23)

Ahora mis preguntas son las siguientes:

  1. me gustaría saber si "todos y cada uno" assertEquals() debe estar en su propio método de prueba? O bien, ¿puedo agrupar casos de prueba similares como testInvalidArguments() para que contenga todos los argumentos que implican caracteres no válidos ya que TODOS arrojan la misma NumberFormatException?

  2. Si prueba un valor de entrada como el carácter ("a"), ¿necesito incluir casos de prueba para TODOS los escenarios? "a" como primer argumento "a" como segundo argumento "a" y "b", como los 2 argumentos

  3. Según mi entendimiento, el beneficio de estas pruebas unitarias es averiguar los casos donde la entrada de un usuario puede fallar y dar como resultado una excepción. Y, luego, podemos darle al usuario un mensaje significativo (pidiéndole que proporcione una entrada válida) en lugar de una excepción. ¿Es eso correcto? Y, ¿es el único beneficio?

  4. ¿Son suficientes los 11 casos de prueba mencionados anteriormente? ¿Me he perdido algo? ¿He exagerado? ¿Cuándo es suficiente?

  5. Siguiendo con el punto anterior, ¿he probado con éxito el método de multiplicación()?

Respuesta

4

1) Existe una compensación entre la granularidad de las pruebas (y por lo tanto, la facilidad de diagnóstico) y la verbosidad del código de prueba de su unidad. Personalmente, estoy feliz de utilizar métodos de prueba de grano relativamente grueso, especialmente una vez que las pruebas y el código probado se han estabilizado. El problema de la granularidad solo es relevante cuando las pruebas fallan. (Si obtengo un error en un caso de prueba de varias afirmaciones, soluciono el primer error y lo repito, o corto temporalmente el caso de prueba según sea necesario para descubrir qué está pasando).

2) Use su sentido común. Según su comprensión de cómo se escribe el código, diseñe sus pruebas para ejercitar todas las subcategorías cualitativamente diferentes. Reconozca que es imposible para probar todas las entradas posibles en todos los casos menos en los más triviales.

3) El objetivo de las pruebas unitarias es proporcionar un nivel de seguridad de que los métodos bajo prueba hacen lo que deben hacer. Lo que esto significa depende del código que se prueba.Por ejemplo, si estoy probando un método sort, la validación de la entrada del usuario es irrelevante.

4) La cobertura parece razonable. Sin embargo, sin una especificación detallada de lo que su clase debe hacer, y el examen de las pruebas unitarias reales, es imposible decir si lo ha cubierto todo. Por ejemplo, ¿se supone que su método debe hacer frente a los caracteres de espacios en blanco iniciales/finales, los números con decimales, los números como "123,456", los números expresados ​​con dígitos no latinos, los números en la base 42?

5) Defina "probado con éxito". Si quieres decir, mis pruebas prueban que el código no tiene errores, entonces la respuesta es un "NO" definitivo. A menos que las pruebas unitarias enumeren todas y cada una de las entradas posibles, no pueden constituir una prueba de corrección. (Y en algunas circunstancias, ni siquiera probar todas las entradas es suficiente.)

En todos los casos menos triviales, las pruebas no pueden demostrar la ausencia de errores. Lo único que puede probar es que hay errores presentes. Si necesita demostrar que un programa no tiene errores, debe recurrir a "métodos formales"; es decir, aplicando técnicas formales de demostración de teoremas a su programa.

Y, como señala otra respuesta, debe dársela a los usuarios reales para ver qué pueden surgir en el camino de una entrada inesperada. En otras palabras ... si los requisitos del usuario establecidos o inferidos son realmente completos y válidos.

5

1) Creo que es una buena idea limitar el número de afirmaciones que realiza en cada prueba. JUnit solo informa el primer error en una prueba, por lo que si tiene múltiples afirmaciones, algunos problemas pueden enmascararse. Es más útil poder ver todo lo que pasó y todo lo que falló. Si tiene 10 assertEquals en una prueba y la primera falla, entonces simplemente no sabe qué hubiera pasado con la otra 9. Esos serían buenos puntos de datos para la depuración.

2) Sí, debe incluir pruebas para todas sus entradas.

3) No es solo la entrada del usuario final la que necesita ser probada.Querrá escribir pruebas para cualquier método público que posiblemente falle. Hay algunas buenas pautas para esto, particularmente con respecto a getters y setters, en el JUnit FAQ.

4) Creo que lo tienes bastante bien cubierto. (Al menos no puedo pensar en otra cosa, pero veo # 5).

5) Proporciónelo a algunos usuarios para que lo prueben. Siempre encuentran datos de muestra que nunca pienso probar. :)

+0

@Bill - esto es una multiplicación. La división por cero no debería ser relevante a menos que la implementación esté haciendo algo extraño. –

+0

@Stephen C: Ah, claro. Me desorienté un poco allí. Gracias, y he editado mi respuesta. –

2

1) Lo mejor es mantener sus pruebas pequeñas y enfocadas. De esta forma, cuando falla una prueba, está claro por qué falló la prueba. Esto generalmente resulta en una sola afirmación por prueba, pero no siempre.

Sin embargo, en lugar de la codificación manual una prueba para cada individuo "escenario válido", es posible que desee echar un vistazo a JUnit 4.4 Teorías (ver el JUnit 4.4 release notes y this blog post), o el corredor de prueba JUnit Parameterized.

Las pruebas y teorías parametrizadas son perfectas para métodos de "cálculo" como este. Además, para mantener las cosas organizadas, podría hacer dos clases de prueba, una para las entradas "buenas" y otra para las entradas "malas".

2) Solo necesita incluir los casos de prueba que cree que son más propensos a exponer cualquier error en su código, no todas las combinaciones posibles de todas las entradas (eso sería imposible como señala WizardOfOdds en sus comentarios). Los tres conjuntos que propusiste son buenos, pero probablemente no probaría más que esos tres. El uso de teorías o pruebas parametrizadas, sin embargo, le permitiría agregar aún más escenarios.

3) Hay muchos beneficios para las pruebas de unidad de escritura, no solo el que usted menciona. Algunos otros beneficios incluyen:

  • Confianza en su código - Usted tiene un alto grado de certeza de que su código es correcto.
  • Confianza en Refactor: puede refactorizar su código y saber que si rompe algo, sus pruebas se lo indicarán.
  • Regresiones: sabrá de inmediato si un cambio en una parte del sistema interrumpe este método en particular involuntariamente.
  • Integridad: las pruebas le obligaron a pensar en las posibles entradas que su método puede recibir y en cómo debería responder el método.

5) Parece que hizo un buen trabajo al plantear posibles escenarios de prueba. Creo que tienes todos los importantes.

+0

+1 - Cosas buenas en el punto n. ° 3. –

+0

@Jim Hurne: No es porque una prueba de unidad pasa que * "SABES que funciona" * [sic]. Cuando se pasa una prueba unitaria, usted sabe que * "no funciona" * y eso es ** muy ** diferente. Este es uno de los errores más comunes que las personas cometen con las pruebas unitarias: confían demasiado en que su código funcione ... (por cierto, tengo tantas líneas de pruebas unitarias como líneas de código, así que no confundas mi punto pruebas unitarias) – SyntaxT3rr0r

+0

@Jim Hurne: De la misma manera, no está de ninguna manera * garantizado * para atrapar cada regresión. Definitivamente hay muchos proyectos (y muchos proyectos de alto perfil que están muy bien evaluados por unidades) donde aparece una falla de regresión que lamentablemente no se detecta en ninguna prueba unitaria. Sin embargo, cuando falla una prueba, * sabes * que acabas de tener una regresión. Pero no está garantizado ** en absoluto ** que vas a ver tu regresión de inmediato. – SyntaxT3rr0r

2

El verdadero número de pruebas es, por supuesto, infinito. Eso no es práctico. Debe elegir casos representativos válidos. Parece que has hecho eso. Buen trabajo.

6

Prueba de la unidad es grande (en el proyecto 200 KLOC estoy trabajando Tengo tantos código de prueba de unidad como el código regular), pero (suponiendo una prueba de unidad correcta):

  • una prueba de unidad que pasa hace no garantía de que su código funciona

creo que de esta manera:

  • una prueba de unidad que no demuestra su código se rompe

Es muy importante darse cuenta de esto.

Además de eso:

  • por lo general es imposible probar cada entrada posible

Y luego, cuando estás refactorización:

  • si todo sus pruebas unitarias están pasando no no significa que no presentó un regresión

Pero:

  • si uno de su prueba de unidad no sabe que ha introducido una regresión

Esto es realmente fundamental y debe ser la unidad de pruebas 101.

+0

+1 a Stephen C y a fastcodejava – SyntaxT3rr0r

+0

+1 a usted también, bien dicho. – fastcodejava

1

Solo quiero agregar, que con las pruebas unitarias, puede obtener aún más si piensa primero en los posibles casos y Después de eso, impleméntelo en la moda de desarrollo impulsado por pruebas, porque esto lo ayudará a mantenerse enfocado en el caso actual y esto le permitirá crear la implementación más fácil posible de manera SECA. También puede usar una herramienta de cobertura de prueba, p. en Eclipse EclEmma, ​​que es realmente fácil de usar y le mostrará si las pruebas han ejecutado todo su código, lo que puede ayudarlo a determinar cuándo es suficiente (aunque esto no es una prueba, solo una métrica). En general, cuando se trata de pruebas unitarias me inspiró mucho el libro Test Driven Development by Example de Kent Becks, lo recomiendo encarecidamente.