2017-08-01 32 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

'Graph <_,_>'はレコードかクラスですか?あなたはそれのための定義を(おそらく剥ぎ取った)定義できますか?実際の出力は表示されますか? – TheQuickBrownFox

答えて

1

FsCheckはそう、あなたが何をする必要があるか、あなたのタイプは%Aフォーマッタでフォーマットされているどのように制御され、テスト出力に文字列にテストパラメータを変換するsprintf "%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が "設定パラメータで失敗したテストデータをフォーマットする機能を提供する"ことができるまでは、テストデータを必要なときにフォーマットするための最良の方法です。

+0

ありがとう、今私は必要な私のグラフを印刷することができます。 1ビット残っています。 FsCheckの出力機能に変更する方法がないと述べました。何らかの形で継承して機能をオーバーライドする方法はありませんか(はい、これは悪い考え方です)。そして、これは、私の質問の2番目の箇条書きについて、私は出力する機会がないということです。私の例でテストが失敗したときに生成されたグラフとそのクローン? –

+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"元グラフ。 printfn "クローングラフ:%A" clonedGraph;結果そのような単純なもの。 – rmunn

関連する問題