2017-08-01 65 views
3

我正在使用VisualStudio中的FsCheck和NUnit進行測試。自定義FsCheck輸出

問題目前是:我設法生成隨機圖(用於測試一些圖形功能),但是當一個測試失敗,FsCheck吐出來的全圖,所以它從字面上轉儲的記錄,你的原始清單它不使用的ToString在那裏看不到任何東西。

另外我不僅需要輸入圖形進行檢查,而且還需要運行屬性時創建的一些其他數據。

因此,如何能更改FsCheck的輸出行爲,以

  • 居然打電話給我的ToString方法輸入圖形
  • 輸出的詳細信息

當測試失敗?編輯: 這是我目前的測試設置。

module GraphProperties 

open NUnit.Framework 
open FsCheck 
open FsCheck.NUnit 

let generateRandomGraph = 
    gen { 
     let graph: Graph<int,int> = Graph<_,_>.Empty() 
     // fill in random nodes and transitions... 
     return graph 
    } 

type MyGenerators = 
    static member Graph() = 
     {new Arbitrary<Graph<int,int>>() with 
      override this.Generator = generateRandomGraph 
      override this.Shrinker _ = Seq.empty } 

[<TestFixture>] 
type NUnitTest() = 
    [<Property(Arbitrary=[|typeof<MyGenerators>|], QuietOnSuccess = true)>] 
    member __.cloningDoesNotChangeTheGraph (originalGraph: Graph<int,int>) = 
     let newGraph = clone originalGraph 
     newGraph = originalGraph 
+2

你怎麼做你的斷言?即使使用NUnit來運行測試,也有不同的方法來斷言。這將有助於提供一個示例測試,甚至是最後一行。 – TheQuickBrownFox

+0

我剛剛添加了一些代碼示例。最難的部分是讓發電機工作。對於屬性本身,我得到隨機輸入,用這個輸入調用待測方法並返回一個布爾值(在這種情況下爲比較結果)。 –

+0

是'圖<_,_>'是一個記錄還是一個類?你能否提供(或許已被剝離)的定義?你看到的實際輸出是什麼? – TheQuickBrownFox

回答

1

FsCheck使用sprintf "%A"測試參數轉換爲字符串測試輸出,所以你需要做的就是控制你的類型是如何由%A格式格式化。根據How do I customize output of a custom type using printf?,要做到這一點的方法是StructuredFormatDisplay attribute。該屬性的值應該是格式爲PreText {PropertyName} PostText的字符串,其中PropertyName應該是您的類型的屬性(而不是函數!)。例如,假設您的樹葉結構中包含一些複雜的信息,但對於您的測試,您只需要知道樹葉的數量,而不是其中的內容。所以你會從這樣的數據類型開始:

// Example 1 
type ComplicatedRecord = { ... } 
type Tree = 
    | Leaf of ComplicatedRecord 
    | Node of Tree list 
    with 
     member x.LeafCount = 
      match x with 
      | Leaf _ -> 1 
      | Node leaves -> leaves |> List.sumBy (fun x -> x.LeafCount) 
     override x.ToString() = 
      // For test output, we don't care about leaf data, just count 
      match x with 
      | Leaf -> "Tree with a total of 1 leaf" 
      | Node -> sprintf "Tree with a total of %d leaves" x.LeafCount 

現在,到目前爲止,這不是你想要的。這種類型的確不是而是具有聲明的自定義%A格式,所以FsCheck(以及其他任何使用sprintf "%A"來格式化的格式)將最終輸出樹的整個複雜結構及其所有不相關的測試葉數據。爲了讓FsCheck輸出你想看到的內容,你需要設置一個屬性,而不是一個函數(ToString不能用於這個目的),它會輸出你想看到的內容。例如: -

// Example 2 
type ComplicatedRecord = { ... } 
[<StructuredFormatDisplay("{LeafCountAsString}")>] 
type Tree = 
    | Leaf of ComplicatedRecord 
    | Node of Tree list 
    with 
     member x.LeafCount = 
      match x with 
      | Leaf _ -> 1 
      | Node leaves -> leaves |> List.sumBy (fun x -> x.LeafCount) 
     member x.LeafCountAsString = x.ToString() 
     override x.ToString() = 
      // For test output, we don't care about leaf data, just count 
      match x with 
      | Leaf -> "Tree with a total of 1 leaf" 
      | Node -> sprintf "Tree with a total of %d leaves" x.LeafCount 

注:我還沒有在F#中測試了這個,只是輸進了堆棧溢出評論框 - 所以它可能是我搞砸ToString()部分。 (我不記得,也無法用Google快速找到,覆蓋是否在with關鍵字之後或之前)。但我知道StructuredFormatDisplay屬性就是你想要的,因爲我自己使用這個來獲得FsCheck的自定義輸出。

順便說一句,你也可以在我的例子中的複雜記錄類型上設置StructuredFormatDisplay屬性。舉例來說,如果你有,你關心的樹形結構,但不是葉子的內容,你會寫一個測試,如:

// Example 3 
[<StructuredFormatDisplay("LeafRecord")>] // Note no {} and no property 
type ComplicatedRecord = { ... } 
type Tree = 
    | Leaf of ComplicatedRecord 
    | Node of Tree list 
    with 
     member x.LeafCount = 
      match x with 
      | Leaf _ -> 1 
      | Node leaves -> leaves |> List.sumBy (fun x -> x.LeafCount) 
     override x.ToString() = 
      // For test output, we don't care about leaf data, just count 
      match x with 
      | Leaf -> "Tree with a total of 1 leaf" 
      | Node -> sprintf "Tree with a total of %d leaves" x.LeafCount 

現在所有的ComplicatedRecord情況下,不管他們的內容,將在輸出中顯示爲文本LeafRecord,您可以更好地專注於樹形結構 - 並且不需要在Tree類型上設置StructuredFormatDisplay屬性。

這不是一個完全理想的解決方案,因爲您可能需要隨時調整StructuredFormatDisplay屬性,正如您正在運行的各種測試所需的那樣。 (對於某些測試,您可能需要關注葉數據的一部分,對於其他人,您希望完全忽略葉數據,等等)。在您投入生產之前,您可能需要將該屬性取出。但是,直到FsCheck獲得「給我一個功能,用」config參數格式化失敗的測試數據,這是讓您的測試數據格式化爲您需要的最佳方式。

+0

謝謝,現在我可以根據需要打印我的圖。剩下一點。您提到無法更改爲FsCheck的輸出功能。是否真的沒有辦法以某種方式繼承和重寫功能(是的,這是邪惡的OO思想)?這是否意味着關於我的問題的第二個問題,我沒有機會輸出例如在我的例子中測試失敗時生成的圖形和它的克隆? –

+0

如果你看看[FsCheck源代碼](https://github.com/fscheck/FsCheck/Blob/master/src/FsCheck/Runner.fs),你會發現'argumentsToString'函數是一個函數調用'sprintf「%A」',然後它又被'onFailureToString'調用。你實際上可以通過實現'IRunner'接口來實現你自己的測試運行者,所以我不完全正確這是唯一的方法。不過,這當然是最簡單的方法。 – rmunn

+0

如果你想輸出原始圖和它的克隆,最簡單的方法就是在你的測試中:'let result =(originalGraph = clonedGraph);如果沒有結果,則printfn「原始圖:%A」originalGraph; printfn「克隆圖:%A」clonedGraph; result'。像這樣簡單。 – rmunn