.net-coref#xunitfsunit

Using `should equal` with sequences in F# and FsUnit


I am using FsUnit.Xunit. I am getting a failure for the following test case:

[<Fact>]
let ``Initialization of DFF`` () =
    dff Seq.empty Seq.empty |> should equal (seq {Zero})

The test failure is:

  Message: 
FsUnit.Xunit+MatchException : Exception of type 'FsUnit.Xunit+MatchException' was thrown.
Expected: Equals seq [Zero]
Actual:   seq [Zero]

  Stack Trace: 
That.Static[a](a actual, IMatcher`1 matcher)
Signal.Initialization of DFF() line 11

I get the same error if the test is:

[<Fact>]
let ``Initialization of DFF`` () =
    dff Seq.empty Seq.empty |> should equal (Seq.singleton Zero)

I have never tested equality of sequences using FsUnit.Xunit, so I am confused what's going on. I'm not even for sure what the failure message is telling me, as it seems to be saying that the expected and actual are the same. I can get this to work fine by converting the sequences to lists, but it would be nice to not have to do that.

Could someone explain what's going on here? It seems I'm not understanding the error message and thus probably something about Equals and comparing sequence values (literals?). Thanks.


Source code to be able to reproduce (I think this is everything):

type Bit =
    | Zero
    | One

type Signal = seq<Bit>

let Nand a b =
    match a, b with
    | Zero, Zero -> One
    | Zero, One  -> One
    | One,  Zero -> One
    | One,  One  -> Zero

let Not input =
    Nand input input

let And a b =
    Not (Nand a b)

let Or a b =
    Nand (Not a) (Not b)

let private liftToSignal1 op (signal: Signal) : Signal =
    Seq.map op signal

let private liftToSignal2 op (signalA: Signal) (signalB: Signal) : Signal =
    Seq.map2 op signalA signalB

let Not' = liftToSignal1 Not

let And' = liftToSignal2 And

let Or' = liftToSignal2 Or

let rec dff data clock : Signal =
    seq {
        yield Zero
        yield! Or' (And' data clock)
                        (And' (dff data clock) (Not' clock))
    }

Solution

  • This is an issue with structural vs. referential equality. In F# seq { 'a' } = seq { 'a' } // false but [ 'a' ] = [ 'a' ] // true due to seq being IEnumerable and not supporting structural equality (or comparison). Lists (and other F# container-like types) are much more 'intelligent', i.e. they support structural equality / comparison if the contained objects support it:

    [ {| foo = StringComparison.Ordinal; bar = Some(1.23) |} ] =
      [ {| foo = StringComparison.Ordinal; bar = Some(1.23) |} ] // true
    

    but don't, if they contain anything that doesn't: [ box(fun() -> 3) ] = [ box(fun() -> 3) ] // false

    So, to make the test work, add a List.ofSeq:

    dff Seq.empty Seq.empty |> List.ofSeq |> should equal [ Zero ]