2016-07-20 41 views
3

我想单元测试一个返回Result(见下文)的函数。如何比较深层嵌套的歧视联盟?

我的问题是:我怎样才能轻松地检查结果在数值上等于预期值?

下面是与精确匹配的版本。

type QuadraticResult = 
    | ComplexResult of Complex * Complex 
    | DoubleResult of float 
    | TwoResults of float * float 


type Result= 
    | QuadraticResult of QuadraticResult 
    | LinearResult of LinearFormulaSolver.Result 

/// Solves a x² + bx + c = 0 
let Compute (a,b,c) : Result = 



[<Fact>] 
member test.``the solution for x² = 0.0 is a double 0.0``()= 
    let result = Compute (1.0, 0.0, 0.0) 
    let expected = Result.QuadraticResult (DoubleResult 0.0) 

    // only exact match, I'd like to test if difference is below a certain threshold 
    Assert.Equal (result, expected) 

这是我迄今使用的解决方案。 它基于Andreys解决方案,但是扩展了允许的距离,结果排列和线性情况。 :

let ComplexEquality distance (x : Complex) (y : Complex)= 
     let dx = x.Real - y.Real 
     let dy = x.Imaginary - y.Imaginary 
     abs (dx) < distance && abs(dy) < distance 


let QuadraticEquality distance x y = match (x,y) with 
         | (ComplexResult (a,b),ComplexResult(c,d)) -> (ComplexEquality distance a c && ComplexEquality distance b d) || (ComplexEquality distance a d && ComplexEquality distance b c) 
         | (DoubleResult a,DoubleResult b) -> abs (a - b) < distance 
         | (TwoResults (a,b),TwoResults(c,d)) -> (abs(a - c) < distance && (b - d) < distance) || (abs(a - d) < distance && (b - c) < distance) 
         | _ -> false 

let LinearEquality distance x y = match (x , y) with 
         | (SingleResult a, SingleResult b) -> abs (a-b) < distance 
         | (NoResults, NoResults) | (InfiniteResults, InfiniteResults) -> true 
         | _ -> false 


let ResultEquality distance x y = match (x,y) with 
         | (QuadraticResult a,QuadraticResult b) -> QuadraticEquality distance a b 
         | (LinearResult a,LinearResult b) -> LinearEquality distance a b 
         | _ -> false 

[<Fact>] 
member test.``the solution for x² = 0 is a double 0``()= 
    let result = QuadraticFormulaSolver.Compute (1.0, 0.0, 0.0) 
    let expected = Result.QuadraticResult (QuadraticFormulaSolver.DoubleResult 0.00001) 

    Assert.True(ResultEquality 0.001 result expected) 

回答

1

我想你只需要编写辅助功能。例如:

open System.Numerics 


type QuadraticResult = 
    | ComplexResult of Complex * Complex 
    | DoubleResult of float 
    | TwoResults of float * float 

type Result= 
    | QuadraticResult of QuadraticResult 
    | LinearResult of int 

let QuadraticEquality x y = match (x,y) with 
          | (ComplexResult (a,b),ComplexResult(c,d)) -> (a.Equals c) && (b.Equals d) 
          | (DoubleResult a,DoubleResult b) -> a = b 
          | (TwoResults (a,b),TwoResults(c,d)) -> (a = b) && (c = d) 
          | _ -> false 

let ResultEquality x y = match (x,y) with 
         | (QuadraticResult a,QuadraticResult b) -> QuadraticEquality a b 
         | (LinearResult a,LinearResult b) -> a = b 
         | _ -> false 

而且在测试中容易写

Assert.IsTrue(ResultEquality result expected); 
+0

我现在正在使用包含在我的问题中的解决方案的增强版本。 – Onur

6

我不认为有什么“魔术”是沃尔德让您自动做到这一点。我觉得你有三种选择:

  1. 编写自定义函数来做到平等的测试,超过float工作在现有的类型和所有嵌套float

  2. 执行特殊的比较写的包装,实现自定义比较,然后使用这种类型的可识别联合内

  3. 编写一些基于反射的魔法来执行自定义平等的测试。

其中,我认为(1)可能是最简单的选择 - 尽管它意味着更多的打字。如果您希望在程序中随处使用此自定义比较,则选项(2)可能会很有意思。最后(3)可能是有意义的,如果你有很多不同的嵌套类型,但它也是最容易出错的选项。

我写的(2)最小的演示,但我仍然认为(1)可能是更好的方法:

[<Struct; CustomComparison; CustomEquality>] 
type ApproxFloat(f:float) = 
    member x.Value = f 
    override x.GetHashCode() = f.GetHashCode() 
    override x.Equals(another) = 
    match another with 
    | :? ApproxFloat as y -> abs (x.Value - y.Value) <= 0.001 
    | _ -> false 
    interface System.IComparable with 
    member x.CompareTo(another) = 
     match another with 
     | :? ApproxFloat as y -> compare x.Value y.Value 
     | _ -> failwith "Cannot compare" 

type Complex = 
    | Complex of ApproxFloat * ApproxFloat 

type Result = 
    | Result of Complex 

Result(Complex(ApproxFloat(1.0), ApproxFloat(1.0))) = 
    Result(Complex(ApproxFloat(1.0001), ApproxFloat(1.0001))) 
+0

谢谢您详细的解答。我只是需要这个简单的单元测试,所以我宁愿不改变我的类型(它并不总是“我的”类型btw。)。是否有一种简单的方法可以获得'float'值,即使它保存在深层中?然后我可以比较浮点值本身。对于复杂的情况(它是.NET库的一部分,所以它不容易改变),我认为'areRoughlyEqual'函数是一个选项 - 但我需要一个快速的方法来到达'Complex'值。 – Onur