Consider unit-testing a module defining strings of constrained length as a type for representing user names:
module UserName
type T = UserName of string
let create (userName : string) =
if userName.Length >= 6 && userName.Length <= 16
then Some (UserName userName)
else None
let apply f (UserName userName) = f userName
let value userName = apply id userName
A unit test to ensure that the function returns None for an invalid input looks simple:
[<Fact>]
let ``UserName must have at least six characters`` () =
UserName.create "aaa" |> should equal None
However, a unit test for the case when the function returns Some appears to require an extra line for completeness of the match expression:
[<Fact>]
let ``Valid UserName`` () =
match UserName.create "validname" with
| Some result ->
UserName.value result |> should equal "validname"
| None -> Assert.True(false)
This does not look right to me, because my tests must define code for testing the "unhappy" path that must yield a failure anyway.
I wish I could write this instead
[<Fact>]
let ``Valid UserName`` () =
UserName.create "validname" |> should equal (Some (UserName "validname"))
but it does not compile (The value or constructor 'UserName' is not defined).
Is there a way to write a unit test of a function returning option<T> that does not require an explicit check of the "unhappy" path (e.g. | None -> Assert.True(false))? I am open to adding more types and/or functions to the UserName module to make it more testable.
Apply
valueto the inside ofSomeand then compare: