将 NUnit 与通用可区分联合一起使用

Using NUnit with generic discriminated unions

我正在尝试使用 FsUnit(在后台使用 NUnit)来测试我的 F# 代码,但它在处理通用可区分联合时遇到了问题。我明白为什么会这样,但我正试图找到一种方法来编写测试而不注释我的期望值。有什么建议吗?是否有更适合此的框架?

type OptionWithReason<'a> =
  | Some of 'a
  | None of string

let reason = "division by 0 is not yet supported"
let safeDivide x y = 
    if y = 0 then 
        None reason
    else
        Some(x/y)

let result = safeDivide 1 0

let expected = None reason
let expectedExplicit: int OptionWithReason = None reason

let test1 = result = expected //true
let test2 = result = expectedExplicit //true

NUnit.Framework.Assert.AreEqual(expectedExplicit,result) //pass
NUnit.Framework.Assert.AreEqual(expected,result) //fail :(
let expected = None reason

这里,编译器无法知道泛型参数的类型'a,所以如果使用就在

中使用
NUnit.Framework.Assert.AreEqual(expected,result)

它将等同于 OptionWithReason<object> 的实例,这将不同于 OptionWithReason<int> 的任何实例,例如 result

至于测试框架,你可以试试Expecto。但是,我仍然认为它会带来同样的问题

我想我找到了。 Unquote 似乎有效。我将它与 xUnit 一起使用只是因为它更容易使用 dotnet 核心库进行设置。我只需要导入库并更改断言:

open Xunit
open Swensen.Unquote

[<Fact>]
let testDUComparison () = 
    test <@ expected = result @>

[<Fact>]
let testDUComparisonExplicit () = 
    test <@ expectedExplicit = result @>

这种方式回避了这个问题,因为我们使用 F# 的相等性而不是像 OptionWithReason<object>

那样将其传递给 C# 库

您的代码中的部分问题是 Assert.AreEqual 将两个参数作为 obj,因此编译器不知道类型应该相同 - 如果它知道这一点,那么它会推断 expectedint OptionWithReason 类型(与 result 匹配)。

解决这个问题的一个简单方法是为 areEqual 定义一个辅助函数,它接受相同类型的参数:

let areEqual (first:'T) (second:'T) = 
  NUnit.Framework.Assert.AreEqual(first, second)

现在您可以将您的断言写成:

areEqual expected result

编译器会推断出 expectedresult 的类型是相同的,它应该可以工作。