Reputation: 53
I would like to do some unit tests on a function that accepts a DU and returns another :
type Commands =
| Schedule of string
| Abandon of string
type Events =
| Scheduled of string
| Abandoned of string
the function is the following :
let exec state = function
| Schedule (cmd) -> Choice1Of2( Scheduled("yes"))
| Abandon(cmd) -> Choice1Of2( Abandoned ("no"))
My tests are as follows :
let result:Choice<Events,string> = exec "initial state" <| Schedule("myCommand");;
result has the following type Choice<Events,string>
, I would have loved to get some quick function in order to use them like this :
assertEvent Scheduled (fun e -> Assert.Equal("should produce GameScheduled Event",gameScheduled, e)) result
But to do that I would have the following home made assert function :
let assertEvent<'TEvent> f g result =
match result with
| Choice1Of2(e) ->
match e with
| f(evt) -> g(evt)
| _ -> Assert.None("event not recognised",Some(e))
| Choice2Of2(reason) -> Assert.None("reason",Some(reason))
I was expecting the function f to allow pattern matching on the fly but it does not. Instead I have the following error :
The pattern disciminator 'f' is not defined
Am I doing somthing wrong somewhere ? my fsharp skills are not that high...
Upvotes: 4
Views: 113
Reputation: 233150
A normal function like f
can't be used as a pattern discriminator, but you can pass Active Patterns around as arguments:
let assertEvent<'TEvent> (|F|_|) g result =
match result with
| Choice1Of2(e) ->
match e with
| F(evt) -> g(evt)
| _ -> Assert.None("event not recognised",Some(e))
| Choice2Of2(reason) -> Assert.None("reason",Some(reason))
This does, however, require you to also pass an Active Pattern as an argument, which is a bit cumbersome:
assertEvent
(function Scheduled(x) -> Some x | _ -> None)
(fun e -> Assert.Equal("should produce GameScheduled Event",gameScheduled, e))
result
This isn't the way I'd approach the problem, though. What I'd prefer is to write a boolean expression that attempts to pull out and compare the values that I want to verify.
For starters, you could create a little generic helper function to pull out one of the choices from Choice<'a, 'b>
:
let toOption1 = function Choice1Of2 x -> Some x | _ -> None
This function has the type Choice<'a,'b> -> 'a option
. (I'll leave it as an exercise to define an equivalent toOption2
function.)
Now you can define a boolean expression that pulls out the data if it's there, and compares it with an expected value:
result
|> toOption1
|> Option.map (function Scheduled x -> x | _ -> "")
|> Option.exists ((=) expected)
This is a boolean expression, so you can use Unquote to turn it into an assertion. This is similar to this approach that I've previously described.
Upvotes: 4