An exception is thrown when requesting F# lists with AutoFixture:
Fixture().Create<int list>()
(* ObjectCreationException: AutoFixture was unable to create an instance
* of type [..] because the traversed object graph contains a circular
* reference.
* Path:
* Microsoft.FSharp.Collections.FSharpList`1[System.Int32] tail -->
* Microsoft.FSharp.Collections.FSharpList`1[System.Int32] -->
* Microsoft.FSharp.Collections.FSharpList`1[System.Int32]
*)
Lists, and other collection types in the System.Collections.Generic namespace, can be filled via the constructor that takes an IEnumerable<T>
as a parameter.
AutoFixture creates such instances via the constructor with the less number of parameters1.
F# lists and the collection types in the System.Collections.Generic namespace are different. One of the differences is the way they’re created:
The following customization can be applied to a Fixture
instance, in order to support the creation of F# lists:
module TestConventions
open System
open Ploeh.AutoFixture
open Ploeh.AutoFixture.Kernel
let (|List|_|) candidate =
match candidate |> box with
| :? Type as t when t.IsGenericType &&
t.GetGenericTypeDefinition() = typedefof<list<_>>
-> Some <| List(t.GetGenericArguments().[0])
| _ -> None
type ReflectiveList =
static member Build<'a>(args : obj list) =
[ for a in args do
yield a :?> 'a ]
static member BuildTyped t args =
typeof<ReflectiveList>
.GetMethod("Build")
.MakeGenericMethod([| t |])
.Invoke(null, [| args |])
let listBuilder (f : IFixture) =
{ new ISpecimenBuilder with
member this.Create(request, ctx) =
match request, ctx with
| List t, _ ->
[ for i in 1..f.RepeatCount -> ctx.Resolve t ]
|> ReflectiveList.BuildTyped t
| _ -> box <| NoSpecimen() }
[<Sealed>]
type ListCustomization() =
interface ICustomization with
member this.Customize fixture =
fixture.Customizations.Add <| listBuilder fixture
let fixture = Fixture().Customize(ListCustomization())
let listOfInts = fixture.Create<int list>()
(* Length = 3
* [0]: 221.0
* [1]: 207.0
* [2]: 129.0
*)
let listOfStrings = fixture.Create<string list>()
(* Length = 3
* [0]: "224f149d-502d-4387-9203-b5e8a7b4e208"
* [1]: "4f2503f4-9bbe-4dd5-ad69-f0e482a41b45"
* [2]: "d973a186-ed30-4c0b-abfa-addc1ac7cc46"
*)
The above can also be written declaratively using AutoData theories. Here’s an example for xUnit.net:
open Xunit.Extensions
open Ploeh.AutoFixture.Xunit
[<Sealed>]
type TestConventionsAttribute() =
inherit AutoDataAttribute(
Fixture().Customize(ListCustomization()))
[<Theory; TestConventions>]
let ``Request a list of ints`` (xs : int list) =
(* Length = 3
* [0]: 234
* [1]: 245
* [2]: 3
*)
()
[<Theory; TestConventions>]
let ``Request a list of strings`` (xs : string list) =
(* Length = 3
* [0]: "ea2a2310-84dc-46e1-aa58-850c57175765"
* [1]: "69d4222b-c272-48e9-871c-a4b2715e1945"
* [2]: "58ba2b15-b33f-46b8-af91-a50f915c3581"
*)
()
ReflectiveList
code is adapted from this snippet by Rick Minerich.This strategy can be overridden. ↩