Moq:如何在没有占位符界面的情况下模拟 F# 中的函数?
Moq: How to mock a function in F# without a placeholder interface?
我的一位同事需要测试某些 F# 函数是否被调用了给定的次数。
在 Moq 中,如果你有一个带有虚拟成员或接口的 class,你通常可以这样做(除非这已经改变,但似乎并非如此),但是 afaik you can hardly mock static methods with Moq for example, which in most cases is how F# functions are compiled to, at least from an IL standpoint. Or, would require to use another library to do so like AutoFake or Pose 而且我不确定 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 中是否有开箱即用的东西来实现同样的事情?
,如下图:
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)
除非我遗漏了什么,否则我不明白您为什么需要任何对象或接口。因为它只是函数,你可以让 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 @>
Mocking 框架在 F# 中偶尔会在某些极端情况下有用,但总的来说,我认为它们的全部目的是弥补 OOP 的不足。除非您正在与某些使用对象和接口的 .NET 库进行交互,否则您很可能没有它们。
我的一位同事需要测试某些 F# 函数是否被调用了给定的次数。
在 Moq 中,如果你有一个带有虚拟成员或接口的 class,你通常可以这样做(除非这已经改变,但似乎并非如此),但是 afaik you can hardly mock static methods with Moq for example, which in most cases is how F# functions are compiled to, at least from an IL standpoint. Or, would require to use another library to do so like AutoFake or Pose 而且我不确定 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 中是否有开箱即用的东西来实现同样的事情?
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)
除非我遗漏了什么,否则我不明白您为什么需要任何对象或接口。因为它只是函数,你可以让 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 @>
Mocking 框架在 F# 中偶尔会在某些极端情况下有用,但总的来说,我认为它们的全部目的是弥补 OOP 的不足。除非您正在与某些使用对象和接口的 .NET 库进行交互,否则您很可能没有它们。