假设我正在定义一个Haskell函数f(可以是纯函数或操作),并且在f的某个地方调用了函数g。例如:
f = ...
g someParms
...
我如何在单元测试中使用mock版本替换函数g?
如果我在Java中工作,g将是SomeServiceImpl
类上的一个方法,该类实现了SomeService
接口。然后,我可以使用依赖注入来告诉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,想知道这是否是最好的方法?更普遍地说,我喜欢依赖注入的想法,不仅用于模拟,还可使代码更具可定制性和可重用性。通常情况下,我喜欢不被锁定在代码使用的任何服务的单个实现中。广泛使用上述技巧以获得依赖注入的好处是否被认为是一个好主意?编辑:
让我们更进一步。 假设我在一个模块中有一系列函数 a、b、c、d、e 和 f,它们都需要能够引用来自另一个模块的函数 g、h、i 和 j。并且假设我想能够模拟函数 g、h、i 和 j。我显然可以将这 4 个函数作为参数传递给 a-f,但是要向所有函数添加 4 个参数有点麻烦。此外,如果我需要更改 a-f 中的任何一个实现以调用另一种方法,则需要更改其签名,这可能会创建一个难以维护的重构练习。
有什么技巧可以使这种类型的情况更容易处理吗?例如,在 Java 中,我可以构造一个对象,并将所有外部服务存储在成员变量中。构造函数将服务存储在成员变量中。然后,任何方法都可以通过成员变量访问这些服务。因此,当向服务添加方法时,不会更改任何方法签名。如果需要新服务,则只需更改构造函数方法签名。