I'd like to check that a value is of a particular case of a discriminated union, without having to also check any included data. My motivation is to only test one thing with each unit test.
An example is as follows (the last two lines give compilation errors):
module MyState
open NUnit.Framework
open FsUnit
type MyState =
| StateOne of int
| StateTwo of int
let increment state =
match state with
| StateOne n when n = 10 -> StateTwo 0
| StateOne n -> StateOne (n + 1)
| StateTwo n -> StateTwo (n + 1)
[<Test>]
let ``incrementing StateOne 10 produces a StateTwo`` ()=
let state = StateOne 10
(increment state) |> should equal (StateTwo 0) // works fine
(increment state) |> should equal (StateTwo _) // I would like to write this...
(increment state) |> should be instanceOfType<StateTwo> // ...or this
Can this be done in FsUnit?
I'm aware of this answer but would prefer not to have to write matching functions for each case (in my real code there are far more than two).
A discriminated union is a union data structure that holds various objects, with one of the objects identified directly by a discriminant. The discriminant is the first item to be serialized or deserialized. A discriminated union includes both a discriminant and a component.
Discriminated unions are useful for heterogeneous data; data that can have special cases, including valid and error cases; data that varies in type from one instance to another; and as an alternative for small object hierarchies.
FsUnit is a set of libraries that makes unit-testing with F# more enjoyable. It adds a special syntax to your favorite . NET testing framework. FsUnit currently supports NUnit, xUnit, and MsTest.
Advertisements. Unions, or discriminated unions allows you to build up complex data structures representing well-defined set of choices. For example, you need to build an implementation of a choice variable, which has two values yes and no. Using the Unions tool, you can design this.
If you don't mind using reflections, the isUnionCase
function from this answer could be handy:
increment state
|> isUnionCase <@ StateTwo @>
|> should equal true
Note that it's a bit verbose because you need a function call before comparing values.
A similar but lighter approach could be comparison of tags:
// Copy from https://stackoverflow.com/a/3365084
let getTag (a:'a) =
let (uc,_) = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(a, typeof<'a>)
uc.Name
increment state
|> getTag
|> should equal "StateTwo"
Beware that this is not type-safe and you can easily misspell a union case name.
What I would do is to create a similar DUs for comparison purpose:
type MyStateCase =
| StateOneCase
| StateTwoCase
let categorize = function
| StateOne _ -> StateOneCase
| StateTwo _ -> StateTwoCase
In this way, you define categorize
once and use it multiple times.
increment state
|> categorize
|> should equal StateTwoCase
It appears FSUnit doesn't (or can't, I'm not sure) directly support this use case.
The next best thing I've found is to declare a TestResult
type like the following and use a match to reduce the result to this type.
type TestResult =
| Pass
| Fail of obj
Here is the reducing match
let testResult =
match result with
| OptionA(_) -> Pass
| other -> Fail(other)
Now you can just use should equal
to ensure the correct result.
testResult |> should equal Pass
The benefits of this solution are strong typing but more importantly in the failure case you can see what the invalid result was.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With