I am testing a round-trip of a Thoth.Json
Encoder / Decoder pair.
It looks like this:
type CustomArbitrary =
static member String() =
Arb.Default.String()
|> Arb.filter (not << isNull)
[<Fact>]
let ``Encode.foo Decode.foo round-trip`` () =
let roundTrip (x : Foo) =
let json =
x
|> Encode.foo
|> Encode.toString 2
let decoded =
json
|> Decode.fromString Decode.foo
Ok x = decoded
// Necessary?
Arb.registerByType (typeof<CustomArbitrary>) |> ignore
Check.QuickThrowOnFailure (roundTrip)
The test fails if I do not filter out null
values for System.String
. However, null
is not a proper value inside Foo
so that is fine.
However, I don't like the usage of Arb.registerByType
here due to global state etc.
How can I rewrite this test so that Arb.registerByType
is not necessary?
Ideally, I would design a FsCheck config once and pass that to each test.
Create the config once like this:
let config =
{
FsCheck.Config.Default with
Arbitrary = [ typeof<CustomArbitrary> ]
}
Then use it to check each test like this:
Check.One(config, roundTrip)
If you switch to Properties
/Property
instead of Fact
, you don't even need an explicit config
instance or the Check
class:
open FsCheck.Xunit
[<Properties(Arbitrary=[| typeof<CustomArbitrary> |])>]
module MyTests =
[<Property>]
let ``Encode.foo Decode.foo round-trip`` (x : Foo) =
let json =
x
|> Encode.foo
|> Encode.toString 2
let decoded =
json
|> Decode.fromString Decode.foo
Ok x = decoded
[<Property>]
let ``Some other test`` () =
true
More info on this approach here.
By the way, be careful about using .
characters in your test names, because some test frameworks (e.g. Visual Studio) use them to define a test hierarchy.