简体   繁体   English

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

[英]Checking function equality in a F# unit test

I have a bunch of F# functions that implement different algorithms for the same input, kind of like the Strategy pattern. 我有一堆F#函数为同一输入实现不同的算法,有点类似于策略模式。 To pick the right strategy, I want to pattern match on the input argument and return the function as a value : 要选择正确的策略,我想在输入参数上进行模式匹配,并将函数作为值返回:

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"))

Now I want to write some unit tests to make sure that I return the right strategy, so I tried something like this in nUnit : 现在我想写一些单元测试以确保我返回正确的策略,所以我在nUnit中尝试过类似的东西:

    [<TestCase("=")>]
    [<Test>]
    member self.getEqualstrategy( relationship:string ) =            
        let strategy = getStrategy relationship

        Assert.AreEqual( strategy, equalStrategy )

Now I think the code is correct and will do what I want, but the assertion fails because functions don't seem to have an equality operation defined on them. 现在我认为代码是正确的并且会做我想要的,但断言失败,因为函数似乎没有在它们上定义相等操作。 so my questions are : 所以我的问题是:

(a) is there a way to compare 2 functions to see if they are the same, ie let isFoo bar = foo == bar, that I can use in an nUnit assertion? (a)有没有办法比较2个函数,看看它们是否相同,即让isFoo bar = foo == bar,我可以在nUnit断言中使用它?

or 要么

(b) is there another unit testing framework that will do this assertion for me in F#? (b)是否有另一个单元测试框架将在F#中为我做这个断言?

Testing whether an F# function returned by your getStrategy is the same function as one of the funcions you defined is also essentially impossible. 测试getStrategy返回的F#函数是否与您定义的函数之一功能相同也基本上是不可能的。

To give some details - the F# compiler generates a class that inherits from FSharpFunc when you return a function as a value. 为了给出一些细节 - 当您将函数作为值返回时,F#编译器会生成一个继承自FSharpFunc的类。 More importantly, it generates a new class each time you create a function value, so you cannot compare the types of the classes. 更重要的是,每次创建函数值时它都会生成一个类,因此您无法比较类的类型。

The structure of the generated classes is something like this: 生成的类的结构是这样的:

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

In principle, you could use Reflection to look inside the Invoke method and find which function is called from there, but that's not going to be a reliable solution. 原则上,您可以使用Reflection查看Invoke方法,并查找从那里Invoke函数,但这不是一个可靠的解决方案。

In practice - I think you should probably use some other simpler test to check whether the getStrategy function returned the right algorithm. 在实践中 - 我认为您应该使用其他更简单的测试来检查getStrategy函数是否返回了正确的算法。 If you run the returned strategy on a couple of sample inputs, that should be enough to verify that the returned algorithm is the right one and you won't be relying on implementation details (such as whether the getStrategy function just returns a named function or whether it returns a new lambda function with the same behaviour. 如果在几个示例输入上运行返回的策略,那么应该足以验证返回的算法是否正确,并且您将不依赖于实现细节(例如getStrategy函数是否仅返回命名函数或是否返回具有相同行为的新lambda函数。

Alternatively, you could wrap functions in Func<_, _> delegates and use the same approach that would work in C#. 或者,您可以将函数包装在Func<_, _>委托中,并使用与C#相同的方法。 However, I think that checking whether getStrategy returns a particular reference is a too detailed test that just restricts your implementation. 但是,我认为检查getStrategy是否返回特定引用是一个过于详细的测试,只会限制您的实现。

Functions doesn't have equality comparer: 函数没有相等比较器:

You will have error: The type '('a -> 'a)' does not support the 'equality' constraint because it is a function type 您将遇到错误: 类型'('a - >'a)'不支持'equality'约束,因为它是一个函数类型

There is a good post here 有一个很好的职位在这里

It would be very difficult for the F# compiler to prove formally that two functions always have the same output (given the same input). F#编译器很难正式证明两个函数总是具有相同的输出(给定相同的输入)。 If that was possible, you could use F# to prove mathematical theorems quite trivially. 如果可能的话,你可以使用F#来非常简单地证明数学定理。

As the next best thing, for pure functions, you can verify that two functions have the same output for a large enough sample of different inputs. 作为下一个最好的事情,对于纯函数,您可以验证两个函数对于足够大的不同输入样本具有相同的输出。 Tools like fscheck can help you automate this type of test. fscheck这样的工具可以帮助您自动化这种类型的测试。 I have not used it, but I've used scalacheck that is based on the same idea (both are ports from Haskell's QuickCheck) 我没有使用它,但我使用了基于相同想法的scalacheck(两者都是来自Haskell的QuickCheck的端口)

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

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