Supongamos que estoy definiendo una función Haskell f (pura o una acción) y en alguna parte dentro de f llamo función g. Por ejemplo:¿Cómo se burla de las pruebas en Haskell?
f = ...
g someParms
...
¿Cómo reemplazo la función g con una versión simulada para la prueba unitaria?
Si estuviera trabajando en Java, g sería un método en la clase SomeServiceImpl
que implementa la interfaz SomeService
. Entonces, usaría la inyección de dependencia para decir f para usar SomeServiceImpl
o MockSomeServiceImpl
. No estoy seguro de cómo hacer esto en Haskell.
es la mejor manera de hacerlo a introducir un tipo de clase SomeService:
class SomeService a where
g :: a -> typeOfSomeParms -> gReturnType
data SomeServiceImpl = SomeServiceImpl
data MockSomeServiceImpl = MockSomeServiceImpl
instance SomeService SomeServiceImpl where
g _ someParms = ... -- real implementation of g
instance SomeService MockSomeServiceImpl where
g _ someParms = ... -- mock implementation of g
Entonces, redefinir f de la siguiente manera:
f someService ... = ...
g someService someParms
...
Parece que esto funcionaría, pero estoy Acabo de enterarme de Haskell y me pregunto si esta es la mejor manera de hacerlo. De manera más general, me gusta la idea de la inyección de dependencia no solo para burlarse, sino también para hacer que el código sea más personalizable y reutilizable. En general, me gusta la idea de no estar encerrado en una sola implementación para ninguno de los servicios que utiliza un fragmento de código. ¿Se consideraría una buena idea usar el truco anterior extensivamente en el código para obtener los beneficios de la inyección de dependencia?
EDIT:
Tomemos un paso más allá. Supongamos que tengo una serie de funciones a, b, c, d, eyf en un módulo que necesitan poder hacer referencia a las funciones g, h, i y j desde un módulo diferente. Y supongamos que quiero poder simular las funciones g, h, i y j. Podría pasar las 4 funciones como parámetros a a-f, pero es un poco molesto agregar los 4 parámetros a todas las funciones. Además, si alguna vez tuviera que cambiar la implementación de alguno de los a-f para llamar a otro método más, necesitaría cambiar su firma, lo que podría crear un desagradable ejercicio de refactorización.
¿Algún truco para hacer que este tipo de situación funcione fácilmente? Por ejemplo, en Java, podría construir un objeto con todos sus servicios externos. El constructor almacenaría los servicios en variables miembro. Entonces, cualquiera de los métodos podría acceder a esos servicios a través de las variables miembro. Por lo tanto, a medida que se agregan métodos a los servicios, ninguna de las firmas de método cambia. Y si se necesitan nuevos servicios, solo cambia la firma del método del constructor.
¿por qué uno querría burlarse de las funciones puras? – yairchu
Buen punto, yairchu. Probablemente solo te burles de las acciones. –
@yairchu Lo haría por razones de eficiencia. Si las pruebas toman x o 100 veces el tiempo es muy importante para la productividad. Así que me gustaría decir que factorial 100000000 = para fines de prueba (por lo tanto, como entrada/simulacro de otra prueba). Pero ese factorial 100000000 = podría ser una prueba en sí misma cuando el corredor de prueba se ejecuta en el módulo factorial. –
user239558