F# 最小起订量 returns "Invalid setup on a static member"

F# Moq returns "Invalid setup on a static member"

在存储库接口文件中

type public IAccountRepository =
    abstract member Find: email:string -> firstname:string -> lastname:string -> Account

在测试文件中

open Moq
let mutable accountRepository = Mock<IAccountRepository>(MockBehavior.Strict)
accountRepository.Setup( fun rep -> rep.Find "aaa" "bbb" "ccc" ).Returns(account) |> ignore

accountRepository.Setup 在运行时引发此错误:

System.NotSupportedException:静态成员的设置无效:rep => FSharpFunc.InvokeFast (FuncConvert.ToFSharpFunc

注意。我不想把我的方法改成 这个:
abstract member Find: email:string * firstname:string * lastname:string

注释 2 [已添加]
我不想像这样使用 Object Expression

let accountRepository_2 = {
    new IAccountRepository with
        member __.Find a b c = account   // <--- this is the only mock I need
        member __.Create x = ()
        member __.Delete x = ()
        member __.FindByMobile x = account
        member __.FindByWallet x y = account
        member __.Read x = account
    }

不是一个可行的解决方案:我有 3 个存储库和 2 个其他提供程序要注入...我认为对于这种情况来说根本不干净。

那个错误是什么?
知道如何模拟该方法吗?


[更新]
我将最小起订量从 4.10.1 更新到 4.11.0。错误改成:

System.NotSupportedException : Unsupported expression: ... => 
FSharpFunc<string, string>.InvokeFast<string, Account>(...

我正在使用 NSubstitute

let accountRepository = Substitute.For<IAccountRepository>()
(accountRepository.Find "aaa" "bbb" "ccc").Returns(account) |> ignore

而且有效。

我不希望有办法让它与最小起订量一起使用。 Moq 库假定代码遵循通常的 C# 编码实践,并且 F# 以柯里化形式编译具有多个参数的方法的方式并不是 C# 定义它的方式。

当您在 F# 中编写以下内容时:

accountRepository.Setup(fun rep -> 
  rep.Find "aaa" "bbb" "ccc")

Moq 实际上看到的东西看起来更像:

accountRepository.Setup(fun rep -> 
  rep.Find("aaa").Invoke("bbb").Invoke("ccc"))

实际上,它甚至比这更糟糕,因为 F# 在编译器可以静态确定参数数量时进行了优化,并将一些调用折叠为 InvokeFast 调用:

accountRepository.Setup(fun rep -> 
  rep.Find("aaa").InvokeFast("bbb", "ccc"))

不知道这一点的工具无法弄清楚这实际上意味着用三个参数调用 Find

我认为最好的选择是更改方法签名(尽管您明确表示不想这样做)。或者,您可以添加一个轻量级包装器用于测试目的。另一种选择是尝试 F# 模拟库 Foq 看看它是否能更好地处理这种情况。