1

以前の質問で、Kurtは型の設定に関するFsCheck のこのコードを教えArbitraryてくれました。

私は次のものを持っていますArbitrary(免責事項:私は自分が何をしているのかわかりません...、まだFsCheckを理解するのが難しいことで有名ですが、私はそれを機能させることに夢中です)、それ自体は私が作成したものの単純化されたバージョンですついさっき:

type MyArb() =
    inherit Arbitrary<DoNotSize<int64>>()
        override x.Generator = Arb.Default.DoNotSizeInt64().Generator

そして、私はそれを指示どおりに使用します:

[<Property(Verbose = true, Arbitrary= [| typeof<MyArb> |])>]
static member  MultiplyIdentity (x: int64) = x * 1L = x

これにより、何かが不足しているという(やや希望に満ちた)エラーメッセージが表示されます。

 System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
  ----> System.Exception : No instances found on type Tests.Arithmetic.MyArb. Check that the type is public and has public static members with the right signature.
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at FsCheck.Runner.checkMethod(Config config, MethodInfo m, FSharpOption`1 target) in C:\Users\Kurt\Projects\FsCheck\FsCheck\src\FsCheck\Runner.fs:line 318
   at FsCheck.NUnit.Addin.FsCheckTestMethod.runTestMethod(TestResult testResult) in C:\Users\Kurt\Projects\FsCheck\FsCheck\src\FsCheck.NUnit.Addin\FsCheckTestMethod.fs:line 100

その Github コードを振り返ってみると、2 つのAtrbitraryクラスがありますが、どちらも継承されておらず、両方とも異なる静的メンバーを持っています。

乱数ジェネレーターを作成し、それを Arbitrary として静的に NUnit テストに割り当てるにはどうすればよいですか?

4

3 に答える 3

4

Property.Arbitraryパラメーターで指定する型には、 type の静的メンバー(場合によっては複数) が必要Arbです。リンクしたコードのように:

type TestArbitrary2 =
   static member NegativeDouble() =
       Arb.Default.Float()
       |> Arb.mapFilter (abs >> ((-) 0.0)) (fun t -> t <= 0.0)

これをコードに適用すると、次のようになります。

 type MyArb() =
    static member m() = Arb.Default.DoNotSizeInt64()

パラメータの意味はProperty.Arbitrary「Arbitrary の実装」ではなく、「型クラスの実装のバケツ」です。

ご覧のとおり、QuickCheck の元の Haskell 実装は、型クラスに依存してさまざまな型の値を提供します。特定の型を「クイック チェック可能」にするためには、その型に対して定義された「任意」クラスのインスタンスが必要です (たとえば、ここにはすべての基本型のインスタンスがあります)。

F# は型クラス自体をサポートしていないため、FsCheck は型クラスを偽装する必要があり、そこで使用されるスキームは次のとおりです。各型クラス インスタンスは、関数テーブルを返す静的メンバーによって表されます。たとえば、Eqtypeclassをシミュレートしたい場合は、次のように定義します。

type Eq<'a> = { eq: 'a -> 'a -> bool; neq: 'a -> 'a -> bool }

type EqInstances() =
   static member ForInt() : Eq<int> = 
      { eq = (=); neq = (<>) }

   static member ForMyCustomType() : Eq<MyCustomType> = 
      { eq = fun a b -> a.CompareTo(b) = 0
        neq = fun a b -> a.CompareTo(b) <> 0 }

ただし、ロードされたすべてのアセンブリのすべての静的メンバーをスキャンすることはできないため (これは法外にコストがかかります)、型を明示的に指定することには少し不便があります (おまけとして、「インスタンス」の可視性を制御できます)。

于 2016-12-06T16:50:37.913 に答える
2

Mark Seemann が説明したアプローチを好む場合は、plain-FsCheck の使用を検討して、FsCheck.Xunit を完全に取り除くこともできます。

module Tests

open FsCheck

let [<Xunit.Fact>] ``Multiply Identity (passing)`` () = 
    Arb.Default.DoNotSizeInt64 ()
    |> Prop.forAll
    <| fun (DoNotSize x) ->
        x * 1L = x
    |> Check.QuickThrowOnFailure

let [<Xunit.Fact>] ``Multiply Identity (failing)`` () = 
    Arb.Default.DoNotSizeInt64 ()
    |> Prop.forAll
    <| fun (DoNotSize x) ->
        x * 1L = -1L |@ sprintf "(%A should equal %A)" (x * 1L) x
    |> Check.QuickThrowOnFailure

xUnit.net テストランナーの出力:

------ Test started: Assembly: Library1.dll ------

Test 'Tests.Multiply Identity (failing)' failed: System.Exception:
    Falsifiable, after 1 test (2 shrinks) (StdGen (2100552947,296238694)):

Label of failing property: (0L should equal 0L)
Original:
DoNotSize -23143L
Shrunk:
DoNotSize 0L

    at <StartupCode$FsCheck>.$Runner.get_throwingRunner@365-1.Invoke(String me..
    at <StartupCode$FsCheck>.$Runner.get_throwingRunner@355.FsCheck-IRunner-On..
    at FsCheck.Runner.check[a](Config config, a p)
    at FsCheck.Check.QuickThrowOnFailure[Testable](Testable property)
    C:\Users\Nikos\Desktop\Library1\Library1\Library1.fs(15,0): at Tests.Multi..

1 passed, 1 failed, 0 skipped, took 0.82 seconds (xUnit.net 2.1.0 build 3179).
于 2016-12-08T04:00:44.023 に答える
2

この質問は、FsCheck の Reflection ベースの API が理想的ではない理由を明確に示しています。私はその API を完全に避ける傾向があるので、代わりに OP プロパティを次のように記述します。

open FsCheck
open FsCheck.Xunit

[<Property>]
let MultiplyIdentity () =
    Arb.Default.DoNotSizeInt64 () |> Prop.forAll <| fun (DoNotSize x) -> x * 1L = x

ディレクティブが示唆するopenように、これは FsCheck.NUnit の代わりに FsCheck.Xunit を使用しますが、API の動作に違いはありません。

このアプローチの利点は、FsCheck を微調整する必要があるたびに静的クラスを実装する必要がないため、タイプセーフで軽量であることです。

于 2016-12-06T17:18:28.157 に答える