In F #, how do you pass a collection to the xUnit InlineData attribute

I would like to use list, array and / or seq as a parameter for xUnit InlineData.

In C #, I can do this:

using Xunit; //2.1.0 namespace CsTests { public class Tests { [Theory] [InlineData(new[] {1, 2})] public void GivenCollectionItMustPassItToTest(int[] coll) { Assert.Equal(coll, coll); } } } 

In F #, I have this:

 namespace XunitTests module Tests = open Xunit //2.1.0 [<Theory>] [<InlineData(8)>] [<InlineData(42)>] let ``given a value it must give it to the test`` (value : int) = Assert.Equal(value, value) [<Theory>] [<InlineData([1; 2])>] let ``given a list it should be able to pass it to the test`` (coll : int list) = Assert.Equal<int list>(coll, coll) [<Theory>] [<InlineData([|3; 4|])>] let ``given an array it should be able to pass it to the test`` (coll : int array) = Assert.Equal<int array>(coll, coll) 

F # code contains the following build errors:

Library1.fs (13, 16): this is not a valid constant expression or custom attribute value

Library1.fs (18, 16): This is not a valid constant expression or custom attribute value

Turning to the 2nd and 3rd test theories.

Can xUnit be used to pass InlineData to collections?

+7
tdd f # xunit
Jan 27 '16 at 0:36
source share
4 answers

InlineDataAttribute relies on the C # params mechanism. This is what the default InlineData syntax allows in C #: -

 [InlineData(1,2)] 

Your version with array construction: -

 [InlineData( new object[] {1,2})] 

- this is just what complier translates above. The moment you go further, you will encounter the same limitations as what the CLI actually allows - the bottom line is that at the IL level, using attribute constructors, it is understood that at compile time everything should be reduced to constants. The F # equivalent of the specified syntax is simple: [<InlineData(1,2)>] , so a direct answer to your question:

 module UsingInlineData = [<Theory>] [<InlineData(1, 2)>] [<InlineData(1, 1)>] let v4 (a : int, b : int) : unit = Assert.NotEqual(a, b) 



I was not able to avoid the riff using the @bytebuster example, though :) If we define an assistant: -

 type ClassDataBase(generator : obj [] seq) = interface seq<obj []> with member this.GetEnumerator() = generator.GetEnumerator() member this.GetEnumerator() = generator.GetEnumerator() :> System.Collections.IEnumerator 

Then (if we want to abandon laziness), we can abuse list to avoid using seq / yield to win the golf code: -

 type MyArrays1() = inherit ClassDataBase([ [| 3; 4 |]; [| 32; 42 |] ]) [<Theory>] [<ClassData(typeof<MyArrays1>)>] let v1 (a : int, b : int) : unit = Assert.NotEqual(a, b) 

But the original seq syntax can be made clean enough, so you don’t need to use it as above, instead:

 let values : obj array seq = seq { yield [| 3; 4 |] yield [| 32; 42 |] } type ValuesAsClassData() = inherit ClassDataBase(values) [<Theory; ClassData(typeof<ValuesAsClassData>)>] let v2 (a : int, b : int) : unit = Assert.NotEqual(a, b) 



However, most of the idioms with xUnit v2 for me is to use the direct MemberData (which is similar to xUnit v1 PropertyData , but generalized to work with fields): -

 [<Theory; MemberData("values")>] let v3 (a : int, b : int) : unit = Assert.NotEqual(a, b) 

The key to getting it right is to place : seq<obj> (or : obj array seq ) in the sequence declaration or xUnit will throw at you.

+5
Feb 01 '16 at 10:19
source share

As described in this question , you can use literals only with InlineData . Lists are not literals.

However, xUnit provides ClassData , which seems to do what you need.

This question discusses the same issue for C #.

To use ClassData with tests, simply create a seq<obj[]> data class:

 type MyArrays () = let values : seq<obj[]> = seq { yield [|3; 4|] // 1st test case yield [|32; 42|] // 2nd test case, etc. } interface seq<obj[]> with member this.GetEnumerator () = values.GetEnumerator() member this.GetEnumerator () = values.GetEnumerator() :> System.Collections.IEnumerator module Theories = [<Theory>] [<ClassData(typeof<MyArrays1>)>] let ``given an array it should be able to pass it to the test`` (a : int, b : int) : unit = Assert.NotEqual(a, b) 

Although this requires some manual coding, you can reuse the data class, which seems to be useful in real projects, where we often run different tests against the same data.

+8
Jan 27 '16 at 1:50
source share

You can use the FSharp.Reflection namespace for a good effect here. Consider the hypothetical function isAnswer: (string β†’ int β†’ bool) that you want to test with a few examples.

Here is one way:

 open FSharp.Reflection open Xunit type TestData() = static member MyTestData = [ ("smallest prime?", 2, true) ("how many roads must a man walk down?", 41, false) ] |> Seq.map FSharpValue.GetTupleFields [<Theory; MemberData("MyTestData", MemberType=typeof<TestData>)>] let myTest (q, a, expected) = Assert.Equals(isAnswer qa, expected) 

The key point is |> Seq.map FSharpValue.GetTupleFields . It takes a list of tuples (you must use tuples to allow different types of arguments) and converts it to the IEnumerable<obj[]> that XUnit expects.

+1
Jun 18 '18 at 8:07
source share

You can also use member data without a class:

 let memberDataProperty:= seq { yield [|"param1":> Object; param2 :> Object; expectedResult :> Object |] } [<Theory>] [<MemberData("memberDataProperty")>] let ''Can use MemberData'' param1 param2 expectedResult = ... 
0
Dec 18 '18 at 22:58
source share



All Articles