【问题标题】:Moq: How to mock a function in F# without a placeholder interface?Moq:如何在没有占位符接口的情况下在 F# 中模拟函数?
【发布时间】:2020-10-10 07:48:10
【问题描述】:

我的一位同事需要测试某些 F# 函数是否被调用了给定的次数。

在 Moq 中,如果您有一个带有虚拟成员或接口的类,您通常可以这样做(除非这已更改,但似乎并非如此),但例如 afaik you can hardly mock static methods with Moq,其中 @ 987654322@,至少从 IL 的角度来看。或者,需要使用另一个库来执行此操作,例如 AutoFakePose,我不确定 F# 支持是否真正得到正确实现。

我们最终创建了一个CallCounter 类型,它将保存要调用的函数和一个计算该函数被调用次数的变量(有点类似于this answer,但有一个实际类型)。

module Tests

open Foq
open Xunit
open Swensen.Unquote


type CallCounter<'Input, 'Output>(f: 'Input -> 'Output) =
    let mutable count = 0
    member this.Count = count
    member this.Invoke(input) =
        count <- count + 1
        f input

type CallOutputs<'Input, 'Output>(f: 'Input -> 'Output) =
    let outputs = ResizeArray()
    member this.Outputs =
        List.ofSeq outputs
    member this.Invoke(input) =
        let output = f input
        outputs.Add(output)
        output

let callFunDepTwice (funDep: unit -> int32) =
    sprintf "%A|%A" (funDep()) (funDep())

[<Fact>]
let ``callFunDepTwice should work1``() =
    let funDep = fun() -> 42
    let funDepCounter = CallCounter(funDep)
    let actual = callFunDepTwice funDepCounter.Invoke
    test <@ actual = sprintf "%A|%A" 42 42 @>
    test <@ funDepCounter.Count = 2 @>

我想知道在 Moq 中是否有开箱即用的东西来实现同样的目标?


I mean without having to rely on creating a placeholder interface with an impl. using object expressions just for the grand sake to hold the function to invoke, in order to make it compliant with Moq,如下:

type ISurrogate<'Input, 'Output> =
    abstract member Invoke: 'Input -> 'Output

[<Fact>]
let ``callFunDepTwice should work2``() =
    let mockConf = Mock<ISurrogate<unit, int32>>().Setup(fun x -> <@ x.Invoke() @>).Returns(42)
    let mock = mockConf.Create()
    let actual = callFunDepTwice mock.Invoke
    test <@ actual = sprintf "%A|%A" 42 42 @>
    Mock.Verify(<@ mock.Invoke() @>, Times.exactly 2)

【问题讨论】:

    标签: .net-core f# moq xunit


    【解决方案1】:

    除非我遗漏了什么,否则我不明白您为什么需要任何对象或接口。由于这只是函数,您可以让funDep 增加一个本地声明的可变计数器作为副作用:

    [<Fact>] let ``callFunDepTwice should work1``() = 
        let mutable count = 0
        let funDep = fun() -> count <- count + 1; 42 
        let actual = callFunDepTwice funDep
        test <@ actual = sprintf "%A|%A" 42 42 @>   
        test <@ count = 2 @>
    

    在某些极端情况下,模拟框架在 F# 中可能偶尔有用,但总的来说,我认为它们的全部目的是为了弥补 OOP 的不足。除非您与一些使用对象和接口的 .NET 库进行交互,否则您很有可能没有它们。

    【讨论】:

    • 我同意你的观点,不过我很难说服自己。
    • 另外,您展示的示例是我们在一些地方已经在做的事情之一,无论如何我们正试图将事情正式化(即指导方针)。感谢您的回答!
    • Soooo... 这就是您要寻找的答案吗?还是我误解了这个问题?
    • 好吧,这么说吧,我心里已经有了答案=]
    • 那么……如果您已经知道如何在没有代理交集的情况下做到这一点,那么您为什么要问这个问题?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-31
    相关资源
    最近更新 更多