假设我正在定义一个 Haskell 函数 f(纯函数或 Action 函数)并且在 f 中的某处调用函数 g。例如:
f = ...
g someParms
...
如何用模拟版本替换函数 g 以进行单元测试?
如果我在 Java 中工作,g 将是实现接口(interface) SomeService
的类 SomeServiceImpl
上的一个方法。然后,我将使用依赖项注入(inject)告诉 f 使用 SomeServiceImpl
或 MockSomeServiceImpl
。我不确定如何在 Haskell 中执行此操作。
最好的方法是引入类型类 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
然后,重新定义 f 如下:
f someService ... = ...
g someService someParms
...
这似乎可行,但我只是在学习 Haskell,想知道这是否是最好的方法?更一般地说,我喜欢依赖注入(inject)的想法,不仅是为了模拟,也是为了让代码更可定制和可重用。一般来说,我喜欢这样的想法,即不被一段代码使用的任何服务锁定在单一实现中。在代码中广泛使用上述技巧以获得依赖注入(inject)的好处是否是一个好主意?
编辑:
让我们更进一步。假设我在一个模块中有一系列函数 a、b、c、d、e 和 f,它们都需要能够引用来自不同模块的函数 g、h、i 和 j。假设我希望能够模拟函数 g、h、i 和 j。我可以清楚地将 4 个函数作为参数传递给 a-f,但是将 4 个参数添加到所有函数中有点痛苦。另外,如果我需要更改任何 a-f 的实现以调用另一个方法,我需要更改它的签名,这可能会产生令人讨厌的重构练习。
有什么技巧可以轻松应对这种情况?例如,在 Java 中,我可以构造一个对象及其所有外部服务。构造函数会将服务存储在成员变量中。然后,任何方法都可以通过成员变量访问这些服务。因此,当方法被添加到服务中时,方法签名都不会改变。如果需要新服务,只需更改构造函数方法签名。
最佳答案
既然可以基于规范的自动化测试,为什么还要使用单元测试? QuickCheck图书馆为你做这件事。它可以使用 Arbitrary
类型类生成任意(模拟)函数和数据。
“依赖注入(inject)”是隐式参数传递的一种退化形式。在 Haskell 中,你可以使用 Reader
, 或 Free
以更 Haskelly 的方式实现同样的事情。
关于testing - 如何在 Haskell 中模拟测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/984372/