2017-10-31 19 views
4

a*c?のようなグロブ構文パターンを生成するカスタムArbitraryを実装しようとしています。私は自分の実装が正しいと思います.Xunitでテストを実行すると、FsCheckはテストデータを生成するために任意のカスタムPatternを使用しているようには見えません。 LINQPadを使用すると、すべてが期待通りに機能します。オリジナル:例外を除いてヌルパターン:System.NullReferenceException 2つのテスト(0が縮む)(StdGen(1884571966,296370531))の後、カスタムFsCheck Xunitで任意の型が壊れていますが、LINQPadと通常のF#プログラムで動作しています

反証:これは出力され

open Xunit 
open FsCheck 

type Pattern = Pattern of string with 
    static member op_Explicit(Pattern s) = s 

type MyArbitraries = 
    static member Pattern() = 
     (['a'..'c']@['?'; '*']) 
     |> Gen.elements 
     |> Gen.nonEmptyListOf 
     |> Gen.map (List.map string >> List.fold (+) "") 
     |> Arb.fromGen 
     |> Arb.convert Pattern string 

Arb.register<MyArbitraries>() |> ignore 

[<Fact>] 
let test() = 
    let prop (Pattern p) = p.Length = 0 
    Check.QuickThrowOnFailure prop 

:ここでは、コードです...

そして、ここで私は出力とともにLINQPadで実行しているコードです:

open FsCheck 

type Pattern = Pattern of string with 
    static member op_Explicit(Pattern s) = s 

type MyArbitraries = 
    static member Pattern() = 
     (['a'..'c']@['?'; '*']) 
     |> Gen.elements 
     |> Gen.nonEmptyListOf 
     |> Gen.map (List.map string >> List.fold (+) "") 
     |> Arb.fromGen 
     |> Arb.convert Pattern string 

Arb.register<MyArbitraries>() |> ignore 

let prop (Pattern p) = p.Length = 0 
Check.Quick prop 

反証は、1回の試験後(0は縮む)(StdGen(1148389153,296370531)):オリジナル:パターン "*"

あなたはFsCheckはxUnitの中Patternためnull値を生成見ることができるように私はGen.elementsGen.nonEmptyListOfを使ってテストデータをコントロールしています。また、何回か実行すると、指定した文字範囲外のテストパターンが表示されます。 LINQPadでは、これらのパターンは正しく生成されます。私はまた、Visual Studio 2017の標準的なF#コンソールアプリケーションと同じものをテストしました。そこにはカスタムArbitraryも期待通りに動作します。

何が問題になりますか? FsCheckはXunitで動作しているときにデフォルトのstringArbitraryに戻っていますか?

あなたが自分で見るために、このレポのクローンを作成することができます

https://github.com/bert2/GlobMatcher

(私は、各テストはそれとうまくいかない複数のカスタムArbitrary sおよびProp.forAllを持つことになりますので、Prop.forAllを使用しない限り。 Prop.forAllのF#バージョンはArbitraryを1つしか受け入れないため、私はそれらをタプル化することができます。

答えて

2

Arb.registerを使用しないでください。このメソッドはグローバルな状態を変更し、xUnit.net 2の組み込み並列処理のサポートにより、実行時は不定になります。

[<Fact>] 
let test() = 
    let prop (Pattern p) = p.Length = 0 
    Check.QuickThrowOnFailure (Prop.forAll (MyArbitraries.Pattern()) prop) 

(私は部分的にメモリからこれを書いているので、私はいくつかを作ったことがあります。このように動作します

あなたはFsCheck.Xunitグルーライブラリを使用したくない場合は、Prop.forAllを使用することができ、 )小さな文法ミス、うまくいけば、これはあなたに進める方法についてのアイデアを与える必要があります。


一方で、あなたはFsCheckを使用することを選択し、場合。xUnitの、あなたはこのように、Property注釈にカスタムArbitrariesを登録することができます。

[<Property(Arbitrary = [|typeof<MyArbitraries>|])>] 
let test (Pattern p) = p.Length = 0 

あなたが見ることができるように、これは決まり文句の多くの世話をします。 Check.QuickThrowOnFailureに電話する必要はありません。

プロパティは配列の配列を取ります。したがって、複数の型がある場合でもこれは動作します。

同じアービトレイの配列で多くのプロパティを記述する必要がある場合は、[<Property>]属性から派生した独自のカスタム属性を作成できます。 Here's an example

type Letters = 
    static member Char() = 
     Arb.Default.Char() 
     |> Arb.filter (fun c -> 'A' <= c && c <= 'Z') 

type DiamondPropertyAttribute() = 
    inherit PropertyAttribute(
     Arbitrary = [| typeof<Letters> |], 
     QuietOnSuccess = true) 

[<DiamondProperty>] 
let ``Diamond is non-empty`` (letter : char) = 
    let actual = Diamond.make letter 
    not (String.IsNullOrWhiteSpace actual) 

私はこのような「登録」Arbitrariesのあまり好きではないんだ、と述べているすべて。私はコンビネータ・ライブラリーの使用を好んでいます。タイプ・セーフなので、タイプ・ベースのメカニズム全体ではないからです。

+0

'MyArbitraries'を登録するために' Property'属性を使用することは、そのトリックを行いました。私はコンビネータを使用していませんでした。なぜなら、それぞれのテストには複数のカスタム仲裁が必要だからです。しかし、Prop.forAllは1つだけを受け入れます。少なくともF#で。 C#バージョン 'Prop.ForAll'は複数の仲裁人のために過負荷になっています。また、 'FsCheck.Xunit.Property'がテスト関数のパラメータを使用して必要なジェネレータを生成する方法も気に入っています。 'Prop.forAll'を使うより読みやすいIMOです。 –

+0

ちょうど 'Prop.forAllFunc2Bool'のソースをチェックして、私がばかだと気づいた。もちろん、プロパティlambdasを使って 'Prop.forAll'の呼び出しをいつでもネストすることができます。すべての「任意」は、最も深いラムダの内部で利用可能です。 –

+0

さて、もう少し掘り下げましたが、 'gen'計算式が本当に私の問題にとって最もクリーンで拡張可能な解決策であることが分かりました。 –

関連する問題