Dot-Net

在 F# 中,如何將集合傳遞給 xUnit 的 InlineData 屬性

  • May 5, 2020

我想使用列表、數組和/或序列作為 xUnit 的 InlineData 的參數。

在 C# 中,我可以這樣做:

using Xunit; //2.1.0

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

在 F# 中,我有這個:

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# 程式碼給出以下建構錯誤:

Library1.fs (13, 16):這不是有效的常量表達式或自定義屬性值

Library1.fs (18, 16):這不是有效的常量表達式或自定義屬性值

參考第 2 和第 3 測試理論。

是否可以使用 xUnit 將集合傳遞給 InlineData 屬性?

InlineDataAttribute依靠 C#params機制。這就是在 C# 中啟用 InlineData 的預設語法的原因:-

[InlineData(1,2)]

您的數組構造版本:-

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

就是編譯器將上述內容翻譯成的內容。再進一步,您將遇到 CLI 實際啟用的相同限制 - 底線是在 IL 級別,使用屬性建構子意味著所有內容都需要在編譯時歸結為常量。上述語法的 F# 等價物很簡單:[<InlineData(1,2)>],因此您的問題的直接答案是:

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

不過,我無法避免重複@bytebuster 的範例:) 如果我們定義一個助手:-

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

然後(如果我們願意放棄懶惰),我們可以濫用list以避免使用seq/yield來贏得程式碼 golf:-

type MyArrays1() = 
   inherit ClassDataBase([ [| 3; 4 |]; [| 32; 42 |] ])

[<Theory>]
[<ClassData(typeof<MyArrays1>)>]
let v1 (a : int, b : int) : unit = Assert.NotEqual(a, b)

但是可以使原始語法seq足夠乾淨,因此不需要像上面那樣使用它,而是我們這樣做:

let values : obj[] seq = 
   seq { 
       yield [| 3; 4 |] 
       yield [| 32; 42 |] // in recent versions of F#, `yield` is optional in seq too
   }

type ValuesAsClassData() = 
   inherit ClassDataBase(values)

[<Theory; ClassData(typeof<ValuesAsClassData>)>]
let v2 (a : int, b : int) : unit = Assert.NotEqual(a, b)

但是,對我來說,xUnit v2 最慣用的方法是直接使用MemberData (類似於 xUnit v1 PropertyData,但也適用於欄位):-

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

正確的關鍵是將: seq<obj>(或: obj[] seq)放在序列的聲明中,否則 xUnit 會扔給您。


xUnit 2 的更高版本包括一個類型化的 TheoryData,它允許您編寫:

type Values() as this =
   inherit TheoryData<int,int>()
   do  this.Add(3, 4)
       this.Add(32, 42)

[<Theory; ClassData(typeof<Values>)>]
let v2 (a : int, b : int) : unit = Assert.NotEqual(a, b)

這也會對每個參數進行類型檢查。

引用自:https://stackoverflow.com/questions/35026735