3

是否可以验证在 Haskell HSpec 中调用了一个函数?

假设我有两个函数foobar可以转换我的数据。

foo :: Stuff -> Stuff
bar :: Stuff -> Stuff

而且我有一个函数, 它根据它是否收到'f'或'b'作为第二个参数并返回应用函数的结果,在Stuff上应用foobar 。

apply :: Stuff -> Char -> Stuff

在我的测试中,我已经全面测试了每个函数 foo 和 bar ,我不想在apply中测试它们的效果。

我可以验证是否调用了函数 foo 或 bar 吗?取决于传递给应用的参数是什么?

4

2 回答 2

3

“我正在考虑更多的 TDD,比如在 OOP 语言中。这样的事情在 Haskell 中可能吗?”

一个更好的问题是“在 Haskell 中是否有必要这样做?” ;-)

[我意识到这不是你真正问的问题。随意忽略这个答案。]

在 OO 语言中,我们构建与其他对象对话以完成其工作的对象。为了测试这样的对象,我们构建了一堆假对象,将真实对象与假对象挂钩,运行我们想要测试的方法,并断言它使用预期的输入调用伪造的方法等。

在 Haskell 中,我们编写函数。纯函数唯一要做的就是接受一些输入并产生一些输出。所以测试的方法就是运行这个东西,给它提供已知的输入并检查它是否返回已知的输出。它在执行此操作的过程中调用了哪些其他函数并不重要;我们只关心答案是否正确。

特别是,我们通常在 OOP 中不这样做的原因是调用一些任意方法可能会导致“真正的工作”发生——读取或写入磁盘文件、打开网络连接、与数据库和其他服务器通信等。如果你'只是测试你的代码的一部分,你不希望测试依赖于某个数据库是否在某个地方的真实网络服务器上运行;您只想测试一小部分代码。

使用 Haskell,我们将任何可能影响现实世界的事物与仅进行数据转换的事物区分开来。测试仅在内存中转换数据的东西非常简单!(总的来说,测试与现实世界交互的代码部分仍然很困难。但希望这些部分现在非常小。)

选择的 Haskell 测试风格似乎是基于属性的测试。例如,如果你有一个求解方程的函数,你编写一个 QuickCheck 属性,它随机生成 100 个方程,并且对于每个方程,它检查返回的数字是否真正解决了原始方程。这是一小部分代码,可以自动测试几乎所有你想知道的东西!(但不完全是:您需要确保“随机”选择的方程式实际上测试了您关心的所有代码路径。)

于 2014-08-07T16:55:32.140 回答
0

(不完全是 Haskell,但很接近。)

fooP = point . foo
-- testable property: forall s. foo s = runIdenity $ fooP s

barP = point . bar
-- similar testable property

fooAndWitness :: Stuff -> Writer String Stuff
fooAndWitness = fooM >> tell "foo"
-- testable property forall s. (foo s, "foo") = runWriter $ fooAndWitness s

barAndWitness :: Stuff -> Writer String Stuff
barAndWitness = barM >> tell "bar"
-- similar testable property

applyOpen :: Pointed p => (Stuff -> p Stuff) -> (Stuff -> p Stuff) -> Stuff -> Char -> p Stuff
applyOpen onF _   x 'f' = onF x
applyOpen _   onB x 'b' = onB x
applyOpen _   _   x _   = point x
-- semi-testable property (must fix p):
-- forall f b s c. let a = applyOn f b s c in a `elem` [f s, b s, point s]
-- In particular, if we choose p carefully we can be, at least stochastically,
-- sure that either f, b, or neither were called by having p = Const [Int], and running several tests
-- where two random numbers are chosen, `f _ = Const $ [rand1]`, and `b _ = Const $ [rand2]`
-- and verifying we get one of those numbers, which could not have been known when applyOpen was written.

applyM = applyOpen fooM barM
-- similar testable property, although but be loose the "rigged" tests for variable f/b, so
-- some of our correctness may have to follow from the definition.

apply = (runIdentity .) . applyM
-- similar testable property and caveat

Pointed是一个介于 Functor 和 Applicative 之间的类型类,并提供与orpoint相同的语义。参数化的唯一规律是:purereturn(. point) . fmap = (point .)

于 2014-08-07T01:11:59.407 回答