繁体   English   中英

使用Foq和F#函数类型

[英]Using Foq with F# function types

例如,我使用F#类型定义来防止我的函数之间存在硬依赖关系

type IType1 = int -> int
type IType2 = int-> string

let func1 (i : int) : int = i * i
let func2 (i : int) : string = i |> string

let higherFunc (dep1 : IType1) (dep2 : IType2) (input : int) : string =
    input |> dep1 |> dep2

let curriedFunc = higherFunc func1 func2
let x = curriedFunc 2

输出x:“4”

显然这是非常人为和简单的,但想象依赖是一个解析器和一个分类器或其他什么。 我正在构建的小功能粒子。

我正在尝试使用Foq来帮助我的单元测试装置。 这是我第一周正确使用F#,我很难找到如何配置这些类型的模拟。

有两件事值得一提:

1 - 如果我使用抽象类,我可以使它工作,但我不想这样做,因为它对于完全相同的最终结果来说更麻烦。 例如

type IType1 = 
    abstract member doSomething : int -> int

type func1 () =
    interface IType1 with
        member this.doSomething (i: int) = i * i

允许我设置一个类似的模拟

let mT1= Mock.With (fun (x : IType1) -> <@ x.doSomething(any()) --> 5 @>)

但我真的不想这样做。

2 - 如果我只是使用

type IType1 = int -> int
let mT1 = Mock.Of<IType1>()

然后我得到一个有效的值,但如果我尝试以任何方式配置它

let mT1= Mock<IType1>.With (fun x -> <@ x(any()) --> 5 @>)

要么

let mT1= Mock<IType1>.With (fun x -> <@ any() --> 5@>)

然后我得到一个例外

System.NotSupportedException : Expected standard function application: Call 

要么

System.NotSupportedException : Expected standard function application: ValueWithName 

我希望我只是愚蠢的语法,并有可能做我想要的。 我已经尝试了我能想到的每个变体,包括.Setup(条件).Create()的变体,我在源代码中找不到任何示例。

我显然可以很容易地制作自己的嘲笑

let mT1 (i : int) : int = 5

因为任何符合int - > int签名的东西都是有效的,但是如果我想检查函数是否已经传递了某个值,我必须进行日志记录步骤等等。这将是很好的Foq做了一些繁重的工作。

编辑我刚注意到根Mock对象在其签名中有“需要引用类型”(即Mock <'TAbstract(需要引用类型)>) - 这是否意味着我没有机会嘲笑值? 如果我不配置模拟,它怎么管理呢?

你不必嘲笑。 如果您的依赖项只是函数类型,您可以只提供函数:

let mT1= fun x -> 5

对象嘲笑的整个概念是(必须)由面向对象的人发明,以弥补对象不能很好地构成(或根本不构成)的事实。 当您的整个系统正常运行时,您可以在现场创建功能。 不需要嘲笑。

如果您真的忙于使用Foq的设备,例如记录和验证(我建议您重新考虑:您的测试会变得更容易,更有弹性),您可以随时将自己作为一个对象,作为您的代理主机功能:

type ISurrogate<'t, 'r> =
    abstract member f: 't -> 'r

// Setup
let mT1 = Mock.Create<ISurrogate<int, int>>()
mT1.Setup(...)...

let mT2 = Mock.Create<ISurrogate<int, string>>()
mT2.Setup...

higherFunc mT1.f mT2.f 42

mT1.Received(1).Call( ... ) // Verification

这样,丑陋仅限于您的测试,并不会使您的生产代码复杂化。

显然,这只适用于单参数函数。 对于具有多个curried参数的函数,您必须对参数进行元组化并将注释包装在注入站点的lambda中:

// Setup
let mT1 = Mock.Create<ISurrogate<int * int, int>>()

higherFunc (fun x y -> mT1.f(x, y)) ...

如果您经常发现这种情况,可以打包lambda创建以供重用:

let inject (s: ISurrogate<_,_>) x y = s.f (x,y)

higherFunc (inject mT1) ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM