Esto es algo ortogonal al contenido de su pregunta, pero aborda directamente la pregunta planteada en el título.
La programación funcional idiomática implica en su mayoría piezas de código sin efectos secundarios, lo que hace que las pruebas unitarias sean más sencillas en general.Definir una prueba unitaria generalmente implica afirmar una propiedad lógica sobre la función bajo prueba, en lugar de construir grandes cantidades de andamios frágiles solo para establecer un entorno de prueba adecuado.
Como ejemplo, digamos que estamos probando las funciones extendEnv
y lookupEnv
como parte de un intérprete. Una buena prueba unitaria para estas funciones verificaría que si ampliamos un entorno dos veces con la misma variable vinculada a valores diferentes, solo el valor más reciente es devuelto por lookupEnv
.
En Haskell, una prueba de esta propiedad podría ser:
test =
let env = extendEnv "x" 5 (extendEnv "x" 6 emptyEnv)
in lookupEnv env "x" == Just 5
Esta prueba nos da cierta seguridad, y no requiere ninguna instalación o desmontaje que no sea la creación de valor env
que nos interesa en la prueba Sin embargo, los valores bajo prueba son muy específicos. Esto solo prueba un entorno particular, por lo que un error sutil podría pasar fácilmente. Preferimos hacer una declaración más general: para todas las variables y valores x
v
y w
, un entorno env
extendió dos veces con x
obligado a v
después x
está obligado a w
, lookupEnv env x == Just w
.
En general, necesitamos una prueba formal (tal vez mecanizada con un asistente de pruebas como Coq, Agda o Isabelle) para demostrar que una propiedad como esta es válida. Sin embargo, podemos conseguir mucho más cerca de especificar los valores de prueba mediante el uso de QuickCheck, una biblioteca disponible para los idiomas más funcionales que generan grandes cantidades de entrada de prueba arbitraria de propiedades que definen funciones como booleanos:
prop_test x v w env' =
let env = extendEnv x v (extendEnv x w env')
in lookupEnv env x == Just w
En el indicador, nos puede tener QuickCheck generar entradas arbitrarias a esta función, y ver si sigue siendo cierto para todos ellos:
*Main> quickCheck prop_test
+++ OK, passed 100 tests.
*Main> quickCheckWith (stdArgs { maxSuccess = 1000 }) prop_test
+++ OK, passed 1000 tests.
QuickCheck utiliza un poco de magia muy agradable (y extensible) para producir estos valores arbitrarios, pero es la programación funcional que hace tener esos valores útiles. Al hacer que los efectos secundarios sean la excepción (perdón) en lugar de la regla, las pruebas unitarias se vuelven menos tarea de especificar manualmente casos de prueba, y más una cuestión de afirmar propiedades generalizadas sobre el comportamiento de sus funciones.
Este proceso te sorprenderá frecuentemente. El razonamiento a este nivel le brinda a su mente más oportunidades de detectar fallas en su diseño, por lo que es más probable que detecte errores incluso antes de ejecutar su código.
Parece, en este caso de todos modos, que debe seguir adelante y probar la unidad eval_symbol() y eval_list(). – Patrick87