首页 文章

Haskell - 断言一个函数被调用

提问于
浏览
3

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

假设我有两个函数 foobar 来转换我的数据 .

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

我有一个函数在 Stuff 上应用 foobar ,具体取决于它是否接收'f'或'b'作为其第二个参数并返回应用函数的结果 .

apply :: Stuff -> Char -> Stuff

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

我可以验证函数foo或bar是否被调用?取决于传递的参数适用?

2 回答

  • 3

    “我正在考虑更多TDD,就像在OOP语言中一样 . 在Haskell中这样的事情是否可能?”

    一个更好的问题是“在Haskell中是否需要这样的东西?” ;-)

    [我意识到这不是你实际问过的问题 . 随意忽略这个答案 . ]

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

    在Haskell中,我们编写函数 . 纯函数唯一做的就是获取一些输入,并产生一些输出 . 因此,测试的方法是运行该事物,为其提供已知输入并检查它是否返回已知输出 . 它在执行此操作过程中调用的其他功能无关紧要;我们所关心的只是答案是否正确 .

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

    使用Haskell,我们将任何可能影响真实世界的东西与刚刚进行数据转换的东西分开 . 测试只是在内存中转换数据的东西是微不足道的! (一般来说,测试与真实世界交互的代码部分仍然很难 . 但希望这些部分现在非常小 . )

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

  • 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的类型类,它为 point 提供与 purereturn 相同的语义 . 这是唯一的法则来自参数: (. point) . fmap = (point .)

相关问题