在F#单元测试中检查功能是否相等

时间:2011-11-22 10:39:04

标签: unit-testing f# nunit

我有一堆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 个答案:

答案 0 :(得分:12)

测试getStrategy返回的F#函数是否与您定义的函数之一功能相同也基本上是不可能的。

提供一些细节 - 当您将函数作为值返回时,F#编译器会生成一个继承自FSharpFunc的类。更重要的是,每次创建函数值时它都会生成一个 new 类,因此您无法比较类的类型。

生成的类的结构如下:

class getStrategy@7 : FSharpFunc<IEnumerable<double>, IEnumerable<double>> {
  public override IEnumerable<double> Invoke(IEnumerable<double> points) {
    // Calls the function that you're returning from 'getStrategy'
    return Test.equalStrategy(points);
  }
}

// Later - in the body of 'getStrategy':
return new getStrategy@7(); // Returns a new instance of the single-purpose class

原则上,您可以使用Reflection查看Invoke方法,并查找从那里调用的函数,但这不是一个可靠的解决方案。

在实践中 - 我认为您应该使用其他更简单的测试来检查getStrategy函数是否返回了正确的算法。如果在几个示例输入上运行返回的策略,那应该足以验证返回的算法是否正确,并且您不会依赖于实现细节(例如getStrategy函数是否正常只返回一个命名函数,或者它是否返回一个具有相同行为的新lambda函数。

或者,您可以将函数包装在Func<_, _>个委托中,并使用与C#相同的方法。但是,我认为检查getStrategy是否返回特定引用是一个过于详细的测试,只会限制您的实现。

答案 1 :(得分:2)

函数没有相等比较器:

您将收到错误:类型'('a - &gt;'a)'不支持'等于'约束,因为它是函数类型

有一篇好文章here

答案 2 :(得分:2)

F#编译器很难正式证明两个函数总是具有相同的输出(给定相同的输入)。如果可能的话,你可以使用F#来非常简单地证明数学定理。

作为下一个最好的事情,对于纯函数,您可以验证两个函数对于足够大的不同输入样本具有相同的输出。像fscheck这样的工具可以帮助您自动化这种类型的测试。我没有使用它,但我使用了基于相同想法的scalacheck(两者都是来自Haskell的QuickCheck的端口)