Probar una lista ... ¿Todos en la misma prueba o una prueba para cada condición?

21

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 :-)
malarres
fuente
2
Los escribiría como casos de prueba separados. Puede usar pruebas parametrizadas si su sistema de prueba lo admite.
jonrsharpe
55
Si escribe sus pruebas en un dado ... Cuando ... Entonces el estilo se hace evidente que realmente deben probarse por separado ...
Robbie Dee
1
Solo me gustaría agregar: OMI, es bueno separar los casos límite (como nulo y vacío) en pruebas separadas, porque a menudo involucran lógica de casos especiales en diferentes implementaciones posibles, y si estas pruebas fallan, se indicarán claramente en de qué manera falla el código bajo prueba (no tendrá que cavar más profundo, o depurar el caso de prueba para descubrir qué está pasando).
Filip Milovanović
1
¿Lista con elementos duplicados?
atayenel

Respuestas:

30

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.

David Arno
fuente
14

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:

  • Las teorías . Una teoría le permite probar una serie de hechos sobre un conjunto de datos. Luego, el marco alimentará sus pruebas con múltiples escenarios de datos de prueba, ya sea por un campo o por un método estático que genera los datos. Si algunos de sus hechos se aplican según algunas condiciones previas y otras, estos marcos no introducen el concepto de una suposición . TuAssume.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.
  • Pruebas parametrizadas . Las pruebas parametrizadas fueron un precursor de las teorías, por lo que puede que no haya esa comprobación de precondición que pueda tener con las teorías. El resultado final es el mismo. Usted ve los resultados, tiene una entrada principal para la prueba en sí y luego una entrada específica para cada punto de datos.
  • Una prueba con múltiples afirmaciones . Lleva menos tiempo hacer la configuración, pero terminas descubriendo problemas poco a poco. Si la prueba es demasiado larga y se prueban demasiados escenarios diferentes, existen dos grandes riesgos: llevará mucho tiempo ejecutarla, y su equipo se cansará de ello y apagará la prueba.
  • Múltiples pruebas con implementación similar . Es importante tener en cuenta que si las afirmaciones son diferentes, las pruebas no se superponen. Sin embargo, esta sería la sabiduría convencional de un equipo centrado en TDD.

No tengo la mentalidad estricta de que solo puede haber una assertdeclaració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.

Berin Loritsch
fuente
5

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 lista ys = f(xs)tiene la misma longitud que xs. 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.

amon
fuente
¿No debería la "señorita" en la última oración ser "encontrar"?
Robbie Dee
@RobbieDee English es ambiguo, fijo.
amon
3

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:

Mi pauta generalmente es que pruebe un CONCEPTO lógico por prueba. puede tener múltiples afirmaciones en el mismo objeto. generalmente serán el mismo concepto que se está probando. Fuente - Roy Osherove

[...]

Las pruebas deben fallar por una sola razón, pero eso no siempre significa que solo debe haber una declaración de Afirmación. En mi humilde opinión, es más importante mantener el patrón "Organizar, Actuar, Afirmar".

La clave es que solo tiene una acción, y luego inspecciona los resultados de esa acción utilizando afirmaciones. Pero es "Organizar, Actuar, Afirmar, Fin de la prueba". Si tiene la tentación de continuar con la prueba realizando otra acción y más afirmaciones después, realice una prueba por separado. Fuente

hijo
fuente
0

En mi opinión, depende de la condición de la prueba.

  • Si su prueba tiene solo 1 condición para configurar la prueba, pero muchos efectos secundarios. multi-aserción es aceptable.
  • Pero cuando tiene múltiples condiciones, significa que tiene múltiples casos de prueba, cada uno debe estar cubierto por una prueba de 1 unidad solamente.
HungDL
fuente
esto se lee más como un comentario, vea Cómo responder
mosquito