unit test unit-testing haskell quickcheck property-based-testing

unit-testing - unit test angular 5



Para qué usar la prueba de propiedad (2)

Me gustaría saber cuál es el objetivo de las pruebas de propiedades, cuál es su punto dulce, dónde se debe usar. Vamos a tener una función de ejemplo que quiero probar:

f :: [Integer] -> [Integer]

Esta función, f , toma una lista de números y cuadrará los números impares y filtrará los números pares. Puedo decir algunas propiedades sobre la función, como

  1. Dada una lista de números pares, devuelva la lista vacía.
  2. Dada una lista de números impares, la lista de resultados tendrá el mismo tamaño que la entrada.
  3. Dado que tengo una lista de números pares y una lista de números impares, al unirlos, mezclarlos y pasar a la función, la longitud del resultado será la longitud de la lista de números impares.
  4. Dado que proporciono una lista de números impares positivos, entonces cada elemento en la lista de resultados en el mismo índice será mayor que en la lista original
  5. Dado que proporciono una lista de números impares y pares, únalos y barajándolos, luego obtendré una lista donde cada número es impar.
  6. etc.

Ninguna de las propiedades comprueba que la función funciona para el caso más simple, por ejemplo, puedo hacer un caso simple, que pasará estas propiedades si implemento la f incorrectamente:

f = fmap (+2) . filter odd

Entonces, si quiero cubrir un caso simple, parece que necesito repetir una parte fundamental del algoritmo en la especificación de la propiedad, o debo usar pruebas basadas en el valor. La primera opción, que tengo, para repetir el algoritmo puede ser útil, si planeo mejorar el algoritmo si planeo cambiar su implementación, por ejemplo, la velocidad. De esta manera, tengo una implementación referencial, que puedo usar para probar de nuevo.

Si quiero verificar que el algoritmo no falla en algunos casos triviales y no quiero repetir el algoritmo en la especificación, parece que necesito algunas pruebas unitarias. Yo escribiría por ejemplo estas verificaciones:

f ([2,5]) == [25] f (-8,-3,11,1) == [9,121,1]

Ahora tengo mucha más confianza en el algoritmo.

Mi pregunta es, ¿las pruebas basadas en la propiedad están destinadas a reemplazar las pruebas unitarias o son complementarias? ¿Hay alguna idea general sobre cómo escribir las propiedades, por lo que son útiles o simplemente depende totalmente de la comprensión de la lógica de la función? Quiero decir, ¿se puede decir que escribir las propiedades de alguna manera es especialmente beneficioso?

Además, ¿debería uno esforzarse por hacer que las propiedades prueben cada parte del algoritmo? Podría poner la cuadratura fuera del algoritmo, y luego probarlo en otro lugar, dejar que las propiedades prueben solo la parte de filtrado, que parece, que lo cubre bien.

f :: (Integer -> Integer) -> [Integer] -> [Integer] f g = fmap g . filter odd

Y luego puedo pasar solo Prelude.id y probar la g otro lugar mediante la prueba unitaria.


Algoritmo de referencia

Es muy común tener una implementación de referencia (posiblemente ineficiente) y una prueba contra eso. De hecho, esa es una de las estrategias de comprobación rápida más comunes al implementar algoritmos numéricos. Pero no todas las partes del algoritmo necesitan una. A veces hay algunas propiedades que caracterizan el algoritmo completamente. El comentario de Ingo es acertado al respecto: estas propiedades determinan los resultados de su algoritmo (hasta el orden y los duplicados). Para recuperar el orden y los duplicados, puede modificar las propiedades para incluir "en la lista resultante truncada después de la posición del elemento fuente" y viceversa en la otra propiedad.

Granularidad de pruebas.

Por supuesto, dada la capacidad de composición de Haskell, es bueno probar cada pequeña parte razonable de un algoritmo por sí mismo. Confío, p /x -> x*x Ej., /x -> x*x filter odd como referencia sin mirar dos veces.

Si debería haber propiedades para cada parte no es tan claro como podría incluir esa parte del algoritmo más adelante y, por lo tanto, hacer que las propiedades sean discutibles. Debido a la pereza de Haskell, eso no es algo común, pero sucede.


¿Qué tal las siguientes propiedades?

  1. Para todos los números impares en la lista de origen, su cuadrado es un elemento de la lista de resultados.
  2. Para todos los números en la lista de resultados, hay un número en la lista de origen cuyo cuadrado es.

Por cierto, es más fácil leer odd que /x -> x % 2 == 1