From 7ba1bfc1f34189e13098f313afd38addf48d60d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:24:42 +0000 Subject: [PATCH 1/2] Initial plan From 6e2aecbe9b4f6a53c8f812aa51ade7bf4d52699d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:28:52 +0000 Subject: [PATCH 2/2] Document NUnit generic inference limitation and test typed Result assertions Co-authored-by: sergey-tihon <1197905+sergey-tihon@users.noreply.github.com> --- docs/NUnit.fsx | 32 +++++++++++++++++++ .../typed.shouldEqualTests.fs | 28 ++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/docs/NUnit.fsx b/docs/NUnit.fsx index f6bb9be3..a74cb343 100644 --- a/docs/NUnit.fsx +++ b/docs/NUnit.fsx @@ -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` even when the expected value is +`Result`. 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 diff --git a/tests/FsUnit.NUnit.Test/typed.shouldEqualTests.fs b/tests/FsUnit.NUnit.Test/typed.shouldEqualTests.fs index 3d45ae08..798c0744 100644 --- a/tests/FsUnit.NUnit.Test/typed.shouldEqualTests.fs +++ b/tests/FsUnit.NUnit.Test/typed.shouldEqualTests.fs @@ -15,6 +15,8 @@ type NeverEqual() = override _.Equals(other) = false override _.GetHashCode() = 1 +type CommitMessage = { Type: string } + [] type ``shouldEqual Tests``() = @@ -91,6 +93,32 @@ type ``shouldEqual Tests``() = member _.``Error "Foo" should equal Error "Foo"``() = Error "Foo" |> shouldEqual(Error "Foo") + [] + member _.``Ok should infer the error type from the expected Result``() = + let expected: Result<_, string> = Ok { Type = "feat" } + Ok { Type = "feat" } |> shouldEqual expected + + [] + member _.``Error should infer the success type from the expected Result``() = + let expected: Result = Error "invalid" + Error "invalid" |> shouldEqual expected + + [] + 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 + |> ignore + + [] + 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 + |> ignore + [] member _.``Error "Foo" should equal fails and have same message``() = (fun () -> Error "Foo" |> shouldEqual(Error "Bar"))