Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/NUnit.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,38 @@ type MyTests () =
member __.setup () =
FSharpCustomMessageFormatter() |> ignore

(**
Type inference with generic values
---------------------------------

`should equal` accepts the actual value as `obj`, so the expected value's type
does not constrain the actual value's type. For example, an unannotated `Ok`
can become `Result<CommitMessage, obj>` even when the expected value is
`Result<CommitMessage, string>`. These are different types and compare unequal,
although NUnit's formatted values can look identical.

Use [FsUnitTyped](FsUnitTyped.html) to keep the expected and actual types linked:
*)
module ``Result type inference`` =
open FsUnitTyped

type CommitMessage = { Type: string }

let expectedTyped: Result<_, string> = Ok { Type = "feat" }

Ok { Type = "feat" } |> shouldEqual expectedTyped

(**
Alternatively, keep `should equal` and annotate the actual value:
*)
(Ok { Type = "feat" } : Result<_, string>) |> should equal expectedTyped

(**
The same limitation applies to `Error` with an unconstrained success type.
Changing `should` to require matching types would break assertions such as
`[1] |> should contain 1`, where actual and expected intentionally differ in type.
*)

(**

Euler - Problem 1
Expand Down
28 changes: 28 additions & 0 deletions tests/FsUnit.NUnit.Test/typed.shouldEqualTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type NeverEqual() =
override _.Equals(other) = false
override _.GetHashCode() = 1

type CommitMessage = { Type: string }

[<TestFixture>]
type ``shouldEqual Tests``() =

Expand Down Expand Up @@ -91,6 +93,32 @@ type ``shouldEqual Tests``() =
member _.``Error "Foo" should equal Error "Foo"``() =
Error "Foo" |> shouldEqual(Error "Foo")

[<Test>]
member _.``Ok should infer the error type from the expected Result``() =
let expected: Result<_, string> = Ok { Type = "feat" }
Ok { Type = "feat" } |> shouldEqual expected

[<Test>]
member _.``Error should infer the success type from the expected Result``() =
let expected: Result<CommitMessage, _> = Error "invalid"
Error "invalid" |> shouldEqual expected

[<Test>]
member _.``Result with inferred error type should fail for a different payload``() =
let expected: Result<_, string> = Ok { Type = "feat" }

(fun () -> Ok { Type = "fix" } |> shouldEqual expected)
|> Assert.Throws<AssertionException>
|> ignore

[<Test>]
member _.``Result with inferred error type should fail for a different case``() =
let expected: Result<_, string> = Ok { Type = "feat" }

(fun () -> Error "invalid" |> shouldEqual expected)
|> Assert.Throws<AssertionException>
|> ignore

[<Test>]
member _.``Error "Foo" should equal fails and have same message``() =
(fun () -> Error "Foo" |> shouldEqual(Error "Bar"))
Expand Down