How to check a case of discriminatory association with FsUnit

I would like to verify that the value refers to a specific case of a discriminatory association, without having to also verify any data included. My motivation is to test only one thing with each unit test.

An example is as follows (the last two lines give compilation errors):

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 

Can this be done in FsUnit?

I know this answer , but I would prefer not to write the corresponding functions for each case (there are much more than two of them in my real code).

+6
source share
4 answers

If you don't mind using reflections, the isUnionCase function from this answer may be convenient:

 increment state |> isUnionCase <@ StateTwo @> |> should equal true 

Note that this is a bit verbose, because you need to call a function call before comparing the values.

A similar but easier approach would be to compare tags:

 // 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" 

Beware that this is not type safe, and you can easily mistype the name of a union case.

What I would do is create similar DUs for the purpose of comparison:

 type MyStateCase = | StateOneCase | StateTwoCase let categorize = function | StateOne _ -> StateOneCase | StateTwo _ -> StateTwoCase 

This way you define categorize once and use it several times.

 increment state |> categorize |> should equal StateTwoCase 
+7
source

It seems that FSUnit (or not, I'm not sure) directly supports this use case.

The next best thing I've found is to declare a TestResult type as follows and use a match to reduce the result for this type.

 type TestResult = | Pass | Fail of obj 

Below is the abbreviation

 let testResult = match result with | OptionA(_) -> Pass | other -> Fail(other) 

Now you can simply use should equal to ensure the correct result.

 testResult |> should equal Pass 

The advantages of this solution are strong typing, but, more importantly, in the event of a failure, you can see what an unacceptable result was .

+1
source

This does not look very elegant, but you can extract the type from the state value:

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

And then use it in the test:

 (increment state) |> should be (instanceOfState <| StateTwo 88) 

Strike> EDIT

Yes, unfortunately, the type is always MyState . Pattern matching or ugly reflection seems to be inevitable.

0
source

What should I do if FsUnit already supports the statement against a specific case of consolidation, although it is limited to values โ€‹โ€‹like Microsoft.FSharp.Core.Choice<_,...,_> ?

Let us use this with an active multi-case template that uses Reflection to check the name of the union.

 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 

Now you need to work:

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

Source: https://habr.com/ru/post/954581/


All Articles