如何使用 xUnit 测试 F# 选项类型

net*_*oid 5 f# xunit.net

我想在 F# 中使用 xUnit 对这段代码进行单元测试。我如何处理这些选项?

来自 Scott Wlaschin 的书:Domain Modeling Made Functional

type UnitQuantity = private UnitQuantity of int
// ^ private constructor

// define a module with the same name as the type
module UnitQuantity =
    /// Define a "smart constructor" for UnitQuantity
    /// int -> Result<UnitQuantity,string>
    let create qty =
        if qty < 1 then
            // failure
            Error "UnitQuantity can not be negative"
        else if qty > 1000 then
            // failure
            Error "UnitQuantity can not be more than 1000"
        else
            // success -- construct the return value
            Ok (UnitQuantity qty)
Run Code Online (Sandbox Code Playgroud)

测试:

let ``Check UnitQuantity.create is one`` () =

    // ARRANGE
    let expected = 1

    // ACT
    //let unitQtyResult = UnitQuantity.create 1
    match UnitQuantity.create 1 with
    | Error msg -> 0
        //printfn "Failure, Message is %s" msg
    | Ok x -> 0
       // let innerValue = UnitQuantity.value actual

    // ASSERT
    //Assert.Equal(expected,actual)
Run Code Online (Sandbox Code Playgroud)

我知道这ACT一切都错了,这就是我挂断电话的地方。我对 F# 选项、xUnit.net 或单元测试的了解不够充分,无法断言函数的实际值。

Cha*_*ger 5

我可能会直接比较结果而不是模式匹配。但是,由于构造函数的原因,您无法为其创建Ok结果。Result<UnitQuantity, string>private

您可以使用内置函数Result.map来映射Ok结果的值。使用UnitQuantity.value您可以映射Result<UnitQuantity, string>Result<int, string>。所以这应该有效:

let expected = Ok 1

let actual = UnitQuantity.create 1 |> Result.map UnitQuantity.value

Assert.Equal(expected, actual)
Run Code Online (Sandbox Code Playgroud)


Sim*_*lds 4

单元测试的一般经验法则是 Act 部分应该是单个语句。

我们想要检查结果的所有内容都是某种形式的断言

所以我们想要断言结果是 anOk<UnitQuantity>还是 an Error<string>

这是模式匹配允许我们非常简洁地测试的地方

let ``Check UnitQuantity.create is one`` () =
    // ARRANGE
    let qty = 1 // The quantity we supply is one
    let expected = qty // We expect to get that value back

    // ACT
    let actual = UnitQuantity.create qty

    // ASSERT

    // Check if we have an Ok or an Error
    match actual with
      | Ok unitQuantity ->
        // If Ok, check the value is what we expect
        let innerValue = UnitQuantity.value unitQuantity
        Assert.Equal(innerValue, expected)

      // If Error raise an AssertException to fail the test
      | Error errorMessage ->
        let error = sprintf "Expected Ok, was Error(%s)." errorMessage
        Assert.True(false, error) // Force an assertion failure with our error message
Run Code Online (Sandbox Code Playgroud)

请注意该UnitQuantity.value方法,它是一个简单的解包函数,您可以将其添加到模块的末尾,UnitQuantity该函数将返回int值,以便您可以轻松比较它

let value (UnitQuantity e) = e
Run Code Online (Sandbox Code Playgroud)

如果你想测试一个option类型,它会非常相似,使用像这样的匹配语句

match actual with
  | Some value ->
    // Do your assertions here
    ()
  | None ->
    // Do your assertions here
    ()
Run Code Online (Sandbox Code Playgroud)