Estoy probando que una función hace lo que se esperaba en una lista. Entonces quiero probar
f(null) -> null
f(empty) -> empty
f(list with one element) -> list with one element
f(list with 2+ elements) -> list with the same number of elements, doing what expected
Para hacerlo, ¿cuál es el mejor enfoque?
- Prueba de todos los casos en la misma prueba (método), bajo el nombre "WorksAsExpected"
- Colocando una prueba para cada caso, teniendo así
- "WorksAsExpectedWhenNull"
- "WorksAsExpectedWhenEmpty"
- "WorksAsExpectedWhenSingleElement"
- "WorksAsExpectedWhenMoreElements"
- Otra opción en la que no estaba pensando :-)
unit-testing
tdd
malarres
fuente
fuente
Respuestas:
La regla general simple que utilizo para realizar un conjunto de pruebas en un caso de prueba, o muchas, es: ¿involucra solo una configuración?
Entonces, si estaba probando que, para múltiples elementos, los procesó todos y obtuvo el resultado correcto, es posible que tenga dos o más afirmaciones, pero solo tengo que configurar la lista una vez. Entonces un caso de prueba está bien.
Sin embargo, en su caso, tendría que configurar una lista nula, una lista vacía, etc. Son configuraciones múltiples. Así que definitivamente crearía múltiples pruebas en este caso.
Como otros han mencionado, esas "pruebas múltiples" podrían existir como un solo caso de prueba parametrizado; es decir, el mismo caso de prueba se ejecuta contra una variedad de datos de configuración. La clave para saber si se trata de una solución viable reside en las otras partes de la prueba: "acción" y "aseverar". Si puede realizar las mismas acciones y afirmaciones en cada conjunto de datos, utilice este enfoque. Si se encuentra agregando
if
's, por ejemplo, para ejecutar código diferente en diferentes partes de esos datos, entonces esta no es la solución. Utilice casos de prueba individuales en ese último caso.fuente
Hay una compensación. Cuanto más empaques en una prueba, más probable será que tengas un efecto cebolla tratando de que pase. En otras palabras, la primera falla detiene esa prueba. No sabrá sobre las otras afirmaciones hasta que arregle la primera falla. Dicho esto, tener un montón de pruebas unitarias que son en su mayoría similares, excepto por el código de configuración, es mucho trabajo ocupado solo para descubrir que algunas funcionan como están escritas y otras no.
Posibles herramientas, basadas en su marco:
Assume.that()
simplemente se salta la prueba para los datos si falla la condición previa. Esto le permite definir "Funciona como se esperaba" y luego simplemente alimentar muchos datos. Cuando ve los resultados, tiene una entrada para las pruebas principales y luego una entrada secundaria para cada pieza de datos.No tengo la mentalidad estricta de que solo puede haber una
assert
declaración en su prueba, pero pongo las restricciones de que todas las afirmaciones deben probar las condiciones posteriores de una sola acción. Si la única diferencia entre las pruebas son los datos, estoy dispuesto a utilizar las funciones de prueba basadas en datos más avanzadas, como las pruebas parametrizadas o las teorías.Considere sus opciones para decidir cuál es el mejor resultado. Diré que "WorksAsExpectedWhenNull" es fundamentalmente diferente a cualquiera de los casos en los que se trata de una colección que tiene un número variable de elementos.
fuente
Esos son casos de prueba diferentes, pero el código para la prueba es el mismo. El uso de pruebas parametrizadas es, por lo tanto, la mejor solución. Si su marco de prueba no admite la parametrización, extraiga el código compartido en una función auxiliar y llámelo desde casos de prueba individuales.
Intente evitar la parametrización a través de un bucle dentro de un caso de prueba, porque eso dificulta determinar qué conjunto de datos causó el error.
En su ciclo TDD rojo-verde-refactor, debe agregar un conjunto de datos de ejemplo a la vez. La combinación de múltiples casos de prueba en una prueba parametrizada sería parte del paso de refactorización.
Un enfoque bastante diferente es la prueba de propiedad . Crearía varias pruebas (parametrizadas) que afirman varias propiedades de su función, sin especificar datos de entrada concretos. Por ejemplo, una propiedad podría ser: para todas las listas
xs
, la listays = f(xs)
tiene la misma longitud quexs
. El marco de prueba generaría listas interesantes y listas aleatorias, y afirmaría que sus propiedades son válidas para todas ellas. Esto se aleja de la especificación manual de ejemplos, ya que la elección manual de ejemplos podría pasar por alto casos interesantes.fuente
Tener una prueba para cada caso es apropiado porque probar un solo concepto en cada prueba es una buena guía que a menudo se recomienda.
Vea esta publicación: ¿Está bien tener múltiples afirmaciones en una sola prueba de unidad? . Hay una discusión relevante y detallada allí también:
[...]
fuente
En mi opinión, depende de la condición de la prueba.
fuente