testing - 如何在 Haskell 中模拟测试?

标签 testing haskell mocking

假设我正在定义一个 Haskell 函数 f(纯函数或 Action 函数)并且在 f 中的某处调用函数 g。例如:

f = ...
    g someParms
    ...

如何用模拟版本替换函数 g 以进行单元测试?

如果我在 Java 中工作,g 将是实现接口(interface) SomeService 的类 SomeServiceImpl 上的一个方法。然后,我将使用依赖项注入(inject)告诉 f 使用 SomeServiceImplMockSomeServiceImpl。我不确定如何在 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/

相关文章:

测试 Elixir/Phoenix 服务模块

ruby-on-rails-3 - 如何使用 RSpec 模拟有错误的模型

haskell - 简单的 TCP 客户端

haskell - 如何使用foldMap实现遍历

python - 在Python中,是否可以在另一个请求中模拟请求?

grails - 如何在Spock中使用Mock在Grails中进行测试?

javascript - Cypress - 比较两个输入的相等性

testing - Nock、本地主机、端口和 redux-observable

haskell - 在 Haskell 中更惯用地从深度优先预购列表构建 BST

python - 如何模拟对 pyspark sql 函数的内部调用