F# 如何与FsUnit一起检查歧视工会的情况?

F# 如何与FsUnit一起检查歧视工会的情况?,f#,discriminated-union,fsunit,F#,Discriminated Union,Fsunit,我想检查一个值是否属于歧视联合的特定情况,而不必检查任何包含的数据。我的动机是在每个单元测试中只测试一件事情 示例如下(最后两行给出了编译错误): 模块MyState 打开NUnit.Framework 开放式FSU MyState类型= |国家一号 |int的状态二 让增量状态= 匹配状态 |当n=10时状态一n->状态二0 |StateOne n->StateOne(n+1) |状态二n->状态二(n+1) [] 让``递增StateOne 10产生一个StateTwo`()= 让state

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

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

模块MyState
打开NUnit.Framework
开放式FSU
MyState类型=
|国家一号
|int的状态二
让增量状态=
匹配状态
|当n=10时状态一n->状态二0
|StateOne n->StateOne(n+1)
|状态二n->状态二(n+1)
[]
让``递增StateOne 10产生一个StateTwo`()=
让state=StateOne 10
(增量状态)|>应等于(状态2 0)//工作正常
(增量状态)|>应该等于(状态二|)//我想写这个。。。
(增量状态)|>应为instanceOfType/…或此
这可以在FsUnit中完成吗

我知道,但不希望为每种情况都编写匹配函数(在我的实际代码中,匹配函数远远不止两个)。

它看起来不太优雅,但您可以从状态值中提取类型:

let instanceOfState (state: 'a) =
    instanceOfType<'a>
let instanceOfState(状态:'a)=

instanceOfType如果您不介意使用反射,则来自的
isUnionCase
函数可能很方便:

increment state 
|> isUnionCase <@ StateTwo @>
|> should equal true
这样,您可以定义一次
分类
,然后多次使用它

increment state
|> categorize
|> should equal StateTwoCase
FSUnit似乎不(或者不能,我不确定)直接支持这个用例

我发现的下一个最好的方法是声明一个
TestResult
类型,如下所示,并使用一个匹配项将结果简化为该类型

type TestResult =
| Pass
| Fail of obj
这是比赛

let testResult =
    match result with
    | OptionA(_) -> Pass
    | other -> Fail(other)
现在您可以使用
应等于
来确保结果正确

testResult  |> should equal Pass
此解决方案的好处是强类型,但更重要的是在失败案例中,您可以看到无效结果是什么

如果已经支持针对特定联合案例的断言,尽管该断言仅限于Microsoft.FSharp.Core.Choice类型的值

让我们用一个多案例活动模式来利用它,它使用反射来检查联合案例名称

open System.Reflection
open Microsoft.FSharp.Reflection

let (|Pass|Fail|) name (x : obj) =
    let t = x.GetType()
    if FSharpType.IsUnion t &&
        t.InvokeMember("Is" + name,
            BindingFlags.GetProperty, null, x, null )
        |> unbox then Pass
    else Fail x
现在应该可以工作了:

increment state
|> (|Pass|Fail|) "StateTwo"
|> should be (choice 1)

实际上,有一种相当简单的方法可以从C#中执行此操作,但在F#中不起作用。这似乎不起作用-即使我使用
StateOne 88
,测试也会通过,因此不检查值是否为
StateTwo
。这不起作用,因为
'a
类型将只是
MyState
。如果
instanceOfState
使用了有关参数
state
的运行时类型信息,那么它就可以工作了,但这意味着它需要以稍微不同的方式工作。。。
let testResult =
    match result with
    | OptionA(_) -> Pass
    | other -> Fail(other)
testResult  |> should equal Pass
open System.Reflection
open Microsoft.FSharp.Reflection

let (|Pass|Fail|) name (x : obj) =
    let t = x.GetType()
    if FSharpType.IsUnion t &&
        t.InvokeMember("Is" + name,
            BindingFlags.GetProperty, null, x, null )
        |> unbox then Pass
    else Fail x
increment state
|> (|Pass|Fail|) "StateTwo"
|> should be (choice 1)