如何用FsUnit检查歧视联盟的情况?

Mar*_*son 8 f# discriminated-union fsunit

我想检查一个值是否是受歧视联合的特定情况,而不必检查任何包含的数据.我的动机是每个单元测试只测试一件事.

示例如下(最后两行给出编译错误):

module MyState

open NUnit.Framework
open FsUnit

type MyState =
    | StateOne of int
    | StateTwo of int

let increment state =
    match state with
    | StateOne n when n = 10 -> StateTwo 0
    | StateOne n -> StateOne (n + 1)
    | StateTwo n -> StateTwo (n + 1)

[<Test>]
let ``incrementing StateOne 10 produces a StateTwo`` ()=
    let state = StateOne 10
    (increment state) |> should equal (StateTwo 0)             // works fine
    (increment state) |> should equal (StateTwo _)             // I would like to write this...
    (increment state) |> should be instanceOfType<StateTwo>    // ...or this
Run Code Online (Sandbox Code Playgroud)

这可以在FsUnit中完成吗?

我知道这个答案,但我不想为每个案例编写匹配的函数(在我的实际代码中,有两个以上).

pad*_*pad 9

如果你不介意使用反射,这个答案isUnionCase功能可以很方便:

increment state 
|> isUnionCase <@ StateTwo @>
|> should equal true
Run Code Online (Sandbox Code Playgroud)

请注意,它有点冗长,因为在比较值之前需要调用函数.

一个类似但更轻的方法可以比较标签:

// Copy from https://stackoverflow.com/a/3365084
let getTag (a:'a) = 
  let (uc,_) = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(a, typeof<'a>)
  uc.Name

increment state 
|> getTag
|> should equal "StateTwo"
Run Code Online (Sandbox Code Playgroud)

请注意,这不是类型安全的,您可以轻松拼错工会案例名称.

我要做的是创建一个类似的DU用于比较目的:

type MyStateCase =
    | StateOneCase
    | StateTwoCase

let categorize = function
    | StateOne _ -> StateOneCase
    | StateTwo _ -> StateTwoCase
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以定义categorize一次并多次使用它.

increment state
|> categorize
|> should equal StateTwoCase
Run Code Online (Sandbox Code Playgroud)