问题描述
例如,我正在使用F#类型定义来防止函数之间的硬依赖关系
I am using F# type definitions to prevent hard dependencies between my functions, for example
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"
显然,这是非常人性化和简单的,但可以想象依赖项是解析器,排序器或其他任何东西.我正在编写的功能更小.
Obviously this is quite contrived and simple but imagine the dependencies are a parser and a sorter or whatever. Smaller grains of functionality that I am composing.
我正在尝试使用Foq来帮助我的单元测试装置.这是我正确使用F#的第一周,而我很难尝试确定如何配置这些类型的模拟.
I am trying to use Foq to help with my unit test fixtures. This is my first week using F# properly and I am having a hard time trying to work out how to configure a mock of these types.
两件事值得一提:
1-如果我使用抽象类,则可以使其工作,但是我不想这样做,因为对于完全相同的最终结果而言,这样做麻烦得多.例如
1 - I can make it work if I use abstract classes, but I don't want to do this as it's so much more hassle for exactly the same end result. For example
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 @>)
但是我真的不想这么做.
but I really don't want to have to do this.
2-如果我只是使用
type IType1 = int -> int
let mT1 = Mock.Of<IType1>()
然后我得到一个有效值,但是如果我尝试以任何方式配置它
then I get back a valid value, but if I try to configure it in any way like
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(conditions).Create()的变体,但在源代码中找不到任何示例.
I am hoping that I'm just being stupid with the syntax and that it is possible to do what I want. I have tried every variation I can think of, including variations of .Setup(conditions).Create(), and I can't find any examples in the source.
我显然可以轻松制作自己的模拟游戏,例如
I can obviously easily make my own mocks like
let mT1 (i : int) : int = 5
作为适合int的任何东西-> int签名将是有效的,但是如果我要检查函数是否已传递某个值,则必须输入日志记录步骤等.希望Foq能够完成一些繁重的工作.
as anything which fits that int -> int signature will be valid, but then if I want to check that the function was passed a certain value for i I have to put in a logging step etc etc.. It would just be nice to have Foq to do some of the heavy lifting.
修改我只是注意到根Mock对象在其签名中具有``需要引用类型''(即Mock )-这是否意味着我没有机会嘲笑值?如果不配置模拟,它将如何管理它?
EditI just noticed that the root Mock object has 'requires reference type' in its signature ( i.e. Mock<'TAbstract(requires reference type)> ) - does that mean I have no chance of mocking values? How come it manages it if I don't configure the mock?
推荐答案
您不必嘲笑.如果您的依赖项只是函数类型,则可以只提供函数:
You don't have to mock. If your dependencies are just function types, you can just provide functions:
let mT1= fun x -> 5
对象模拟的整个概念是(必须)由面向对象的人们发明的,以弥补对象组成不好(或根本不组成)这一事实.当整个系统正常运行时,您可以当场创建功能.无需嘲笑.
The whole concept of object mocking was (had to be) invented by the object-oriented people to compensate for the fact that objects don't compose well (or at all). When your whole system is functional, you can just create functions on the spot. No mocking necessary.
如果您真的想使用Foq的功能,例如日志记录和验证(我敦促您重新考虑:您的测试将变得更加容易且更具弹性),则始终可以使自己成为一个可以替代的对象托管您的功能:
If you're really hung up on using Foq's facilities like logging and verifying (which I urge you to reconsider: your testing would come out easier and more resilient), you can always make yourself an object that would act as a surrogate host for your functions:
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
通过这种方式,丑陋仅限于您的测试,并且不会使您的生产代码复杂化.
This way, the ugliness is confined to your tests and does not complicate your production code.
显然,这仅适用于单参数函数.对于具有多个咖喱参数的函数,您必须对参数进行元组化并将调用包装在注入位置的lambda中:
Obviously, this will only work for single-argument functions. For functions with multiple curried arguments, you'll have to tuple the arguments and wrap the call in a lambda at injection site:
// Setup
let mT1 = Mock.Create<ISurrogate<int * int, int>>()
higherFunc (fun x y -> mT1.f(x, y)) ...
如果您发现这种情况经常发生,则可以打包lambda创建以供重复使用:
If you find this happening often, you can pack the lambda creation for reuse:
let inject (s: ISurrogate<_,_>) x y = s.f (x,y)
higherFunc (inject mT1) ...
这篇关于将Foq与F#函数类型一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!