2016-07-20 62 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