我有一堆F#函数为同一输入实现不同的算法,有点类似于策略模式 . 要选择正确的策略,我想在输入参数上进行模式匹配,并将函数作为值返回:
let equalStrategy points : seq<double> =
...
let multiplyStrategy factor (points: seq<double>) =
...
let getStrategy relationship =
match relationship with
| "=" -> equalStrategy
| "*5" -> multiplyStrategy 5.0
| _ -> raise (new System.NotImplementedException(" relationship not handled"))
现在我想写一些单元测试以确保我返回正确的策略,所以我在nUnit中尝试过类似的东西:
[<TestCase("=")>]
[<Test>]
member self.getEqualstrategy( relationship:string ) =
let strategy = getStrategy relationship
Assert.AreEqual( strategy, equalStrategy )
现在我认为代码是正确的并且会做我想要的,但断言失败,因为函数似乎没有在它们上定义相等操作 . 所以我的问题是:
(a)有没有办法比较2个函数,看它们是否相同,即让isFoo bar = foo == bar,我可以在nUnit断言中使用?
要么
(b)是否有另一个单元测试框架将在F#中为我做这个断言?
3 回答
测试
getStrategy
返回的F#函数是否与您定义的函数之一功能相同也基本上是不可能的 .To give some details - 当您将函数作为值返回时,F#编译器会生成一个继承自
FSharpFunc
的类 . 更重要的是,每次创建函数值时它都会生成一个新类,因此您无法比较类的类型 .生成的类的结构是这样的:
原则上,您可以使用Reflection查看
Invoke
方法并查找从那里调用的函数,但这不是一个可靠的解决方案 .In practice - 我认为您应该使用其他更简单的测试来检查
getStrategy
函数是否返回了正确的算法 . 如果在几个示例输入上运行返回的策略,那应该足以验证返回的算法是否正确,并且您将不依赖于实现细节(例如getStrategy
函数是否只返回命名函数或是否返回具有相同行为的新lambda函数 .或者,您可以将函数包装在
Func<_, _>
委托中,并使用与C#相同的方法 . 但是,我认为检查getStrategy
是否返回特定引用是一个过于详细的测试,只会限制您的实现 .函数没有相等比较器:
您将收到错误:类型'(' a - > 'a)'不支持'equality'约束,因为它是一个函数类型
有一个好帖子here
F#编译器很难正式证明两个函数总是具有相同的输出(给定相同的输入) . 如果可能的话,你可以使用F#来非常简单地证明数学定理 .
作为下一个最好的事情,对于纯函数,您可以验证两个函数对于足够大的不同输入样本具有相同的输出 . 像fscheck这样的工具可以帮助您自动化这种类型的测试 . 我还没用过它,但是've used scalacheck that is based on the same idea (both are ports from Haskell'的QuickCheck)