0

我创建了一些基本类型,目的是无法创建无效状态。例如:

type PositiveDecimal = private PositiveDecimal of decimal

[<RequireQualifiedAccess>]
module PositiveDecimal =

    let create num =
        if num >= 0m then
            num |> PositiveDecimal |> Ok
        else
            Error.validation None (sprintf "'%f' should be positive." num)

这将返回Result带有成功创建的对象或自定义错误类型的 a。

我发现单元测试很尴尬(使用 MSTest):

[<TestMethod>]
member __.Create() =
    let actual = 
        PositiveDecimal.create 1m
        |> Result.map PositiveDecimal.value

    // This explicit type is unfortunately required because otherwise
    // they will have different error types :(.
    let expected : Result<_, Error.Error> = Ok 1m

    Assert.AreEqual(expected, actual)

如您所见,我必须将其映射回 a decimal,因为我无法PositiveDecimal通过另一个源创建 a。

当然,我可以使用 make 构造函数internal和 use InternalsVisibleTo,但是如果允许整个程序集忽略工厂,这会绕过整个安全点。有没有更好的方法来测试这个?

4

1 回答 1

1

我可能不会为像你这样的智能构造函数写一个测试,但如果我正在写一个,我可能会完全避免这个问题,并有一个测试来检查是否成功论据,即

member __.Create() =
    let res = PositiveDecimal.create 1m

    Assert.True(match res with Ok pd -> PositiveDecimal.value pd = 1M | _ -> false)

另一个带有否定论据的失败案例(可以说是零)。

或者将两者结合到 FsCheck 属性中:

open FsCheck

let prop x = 
    let res = PositiveDecimal.create x
    match res with
    | Ok pd -> x >= 0M && PositiveDecimal.value pd = x 
    | Error _ -> x < 0M

Check.Quick prop
于 2019-08-13T00:25:19.760 回答