如何在具有外部依赖性的f#中测试函数
我很难用外部依赖性对F#代码进行单元测试 在C#(我的背景)中,通常会有一个传入了依赖项的类,然后该类会被重用。为我的示例代码道歉,它很愚蠢,但我只是想说明我的观点如何在具有外部依赖性的f#中测试函数,f#,F#,我很难用外部依赖性对F#代码进行单元测试 在C#(我的背景)中,通常会有一个传入了依赖项的类,然后该类会被重用。为我的示例代码道歉,它很愚蠢,但我只是想说明我的观点 公共类Foo{ 独立性d; 公共Foo(IDependency d){this.d=d;} public int DoStuff(字符串条){返回d.DoSomethingToStuff(条);} 公共int-DoMoreStuff(字符串条){ int i=d.DoSomethingToStuff(巴); 返回d.DoSometh
公共类Foo{
独立性d;
公共Foo(IDependency d){this.d=d;}
public int DoStuff(字符串条){返回d.DoSomethingToStuff(条);}
公共int-DoMoreStuff(字符串条){
int i=d.DoSomethingToStuff(巴);
返回d.DoSomethingElseToStuff(bar,i);
}
}
我试图在F#上保持务实,避免使用类和接口(除非我需要与其他.NET语言进行互操作)
所以在这个场景中,我的方法是让模块和一些依赖项作为函数传入的函数。我找到了这个技术
我在这段代码中遇到的两个问题是:
somethingFunc
,这种情况会多么快失控module Foo =
let doStuff somethingFunc bar =
somethingFunc bar
let doMoreStuff somethingFunc somethingElseFunc bar =
let i = somethingFunc bar
somethingElseFunc bar i
这将使您能够使用部分函数应用程序编写函数:
let doStuff' = Foo.doStuff somethingImp
现在,doStuff'
是一个闭包,因为它关闭了具体的函数somethingImp
。本质上,它捕获了依赖项,因此它的工作方式与具有注入依赖项的对象一样,您仍然可以使用剩余的bar
参数调用它:
let bar = 42
let actual = doStuff' bar
测试 以下是使用本地函数作为存根的示例:
module Tests =
let ``Data flows correctly through doMoreStuff`` () =
let somethingFunc bar =
assert (bar = 42)
1337
let somethingElseFunc bar i =
assert (bar = 42)
assert (i = 1337)
"Success"
let actual = Foo.doMoreStuff somethingFunc somethingElseFunc 42
assert (actual = "Success")
这里,为了简单起见,我使用了,但是为了正确的测试,您应该定义一个正确的断言函数,或者使用您最喜欢的断言库
通常情况下,我倾向于放松输入参数的验证,因为这可能会使测试与特定实现紧密耦合。另外,请记住,您应该—在本例中,只有查询,所以所有的测试双精度都是存根:尽管它们在调用输入时会验证输入,但测试根本不会验证它们是否被调用。人们可能不喜欢此注释,但是:了解副作用并查看。“我试图对F#保持务实态度,避免使用类和接口”相关:@MauricioScheffer我读过几篇博客文章,内容涉及将F#视为函数语言,并使用在数据结构上操作的函数,而不是封装数据和行为。无论如何,我不想把F#当作一种经典的OO语言来对待。@VincePanuccio OO与否没有多大关系。。。保持代码的可组合性,并尽可能避免副作用。@VincePanuccio问题是没有这样的限制。在脑海中分离这三个概念很有用:对象、命令、副作用。如何进行单元测试以确保调用函数?虽然通过这个例子很容易看出,但对于更复杂的逻辑,我想验证某些函数是否被调用。@VincePanuccio更新了我的答案。@VincePanuccio“我如何进行单元测试以确保函数被调用?”@MauricioScheffer这是最好的方法,但这要求您在设计时不进行变异,我相信你知道怎么做:)不过,F#不是一种“纯粹”的函数式语言,有时候,验证命令是否被调用是有帮助的——这就是我在文章中解释的。TBC:在纯函数程序中,没有命令,因此不需要模拟,所以我并不反对。在一个不纯的函数程序中,您可能有一些命令,因此也有一些模拟。
module Tests =
let ``Data flows correctly through doMoreStuff`` () =
let somethingFunc bar =
assert (bar = 42)
1337
let somethingElseFunc bar i =
assert (bar = 42)
assert (i = 1337)
"Success"
let actual = Foo.doMoreStuff somethingFunc somethingElseFunc 42
assert (actual = "Success")