为什么从 F# 调用 Moq 会抛出异常?

Why does this call to Moq from F# throw an exception?

我认为这与在 Verify() 上使用 times 参数有关。

open NUnit.Framework
open Moq

type IService = abstract member DoStuff : unit -> unit

[<Test>]
let ``Why does this throw an exception?``() =
    let mockService = Mock<IService>()
    mockService.Verify(fun s -> s.DoStuff(), Times.Never())

异常消息:

System.ArgumentException : Expression of type 'System.Void' cannot be used for constructor parameter of type 'Microsoft.FSharp.Core.Unit'

Moq 的 Verify 方法有许多重载,如果没有注释,F# 将默认将您指定的表达式解析为期望 Func<IService,'TResult> 的重载,其中 'TResult 是单位,这解释了运行时失败。

你想要做的是显式地使用 Verify 的重载,它需要一个 Action

一种选择是使用 Moq.FSharp.Extensions project (available as a package on Nuget),它添加了 2 个扩展方法 VerifyFuncVerifyAction,从而更容易将 F# 函数解析为基于 Moq 的 C# ActionFunc 个参数:

open NUnit.Framework
open Moq
open Moq.FSharp.Extensions

type IService = abstract member DoStuff : unit -> unit

[<Test>]
let ``Why does this throw an exception?``() =
   let mockService = Mock<IService>()
   mockService.VerifyAction((fun s -> s.DoStuff()), Times.Never())

另一种选择是使用 Foq, a Moq like mocking library specifically designed for F# users (also available as a Nuget package):

open Foq

[<Test>]
let ``No worries`` () =
  let mock = Mock.Of<IService>()
  Mock.Verify(<@ mock.DoStuff() @>, never)