F#参数化xUnit测试
Parameterized xUnit Tests with F#
InlineData
open Xunit
open Xunit.Abstractions
type ParameterizedxUnitTest(output:ITestOutputHelper) =
[]
[]
[]
member _.``add 1 2 equals 3``(a: int, b: int, expected: int) =
let actual = a + b
Assert.Equal(expected, actual)
InlineData
的限制是只能使用字面量常数。因为属性的参数必须是字面量。
The minute you go further, you'll run into the same restrictions on what the CLI will actually enable - the bottom line is that at the IL level, using attribute constructors implies that everything needs to be boiled down to constants at compile time.
而ClassData
和MemberData
不要求使用字面量,但是如果在运行器能显示每行,每个参数必须使用基元类型或基元数组类型。
ClassData
用法是定义一个类型,其继承自ClassDataBase
,其定义在FSharp.xUnit
中。
open FSharp.xUnit
type MyArrays1() =
inherit ClassDataBase([
[| 3; 4 |];
[| 32; 42 |]
])
type ClassDataBaseTest(output:ITestOutputHelper) =
[]
[)>]
member _.v1 (a : int, b : int) =
Assert.NotEqual(a, b)
MemberData
However, most idiomatic with xUnit for me is to use straight MemberData
. 因为参数表每个参数可以有不同的类型,you have to use tuples to allow different arguments types. You can use the FSharp.Reflection
namespace to good effect here.
引用相同类型,请提供成员的名称:
type MemberDataTest(output:ITestOutputHelper) =
static member samples =
[
[|"Homer";""|],"Homer"
[|"Marge";""|],"Marge"
]
|> Seq.map FSharpValue.GetTupleFields
[]
[]
member _.``array different types``(a:string[], b) =
let c = a.[0]
Assert.Equal(c, b)
引用不同类型中的数据,请提供参数MemberType
:
open FSharp.Reflection
type TestData() =
static member MyTestData =
[
"smallest prime?", 2, true
"how many roads must a man walk down?", 41, false
]
|> Seq.map FSharpValue.GetTupleFields
type MemberDataTest(output:ITestOutputHelper) =
[)>]
member _.myTest(q, a, expected) =
let isAnswer (q:string) a =
q.Split(" ").Length = a
Assert.Equal(isAnswer q a, expected)
上面两个示例,The key thing is the line
|> Seq.map FSharpValue.GetTupleFields
It takes the list of tuples and transforms it to the seq
that XUnit expects.
用例
此用例,使用Map
绕过xUnit不支持的数据类型,在测试资源管理器显示MemberData
多行。
type UnifyVoidElementTest(output:ITestOutputHelper) =
let show res =
res
|> Render.stringify
|> output.WriteLine
static let source = [
"
",[{index= 0;length= 5;value= TagSelfClosing("br",[])}]
"
",[{index= 0;length= 3;value= TagStart("p",[])};{index= 3;length= 4;value= TagSelfClosing("br",[])};{index= 12;length= 4;value= TagEnd "p"}]
]
static let mp = Map.ofList source
static member keys =
source
|> Seq.map (fst>>Array.singleton)
[]
member _.``self closing``(x:string) =
let y =
x
|> SeniorTokenizer.tokenize
|> Seq.choose (HtmlTokenSeniorUtils.unifyVoidElement)
|> Seq.toList
let a = mp.[x]
show y
Should.equal y a
此例中,所有的数据都收集在source
中,构成一个行列表。每行是一个记录元组,第一项是输入数据,字符串类型。第二项输出数据,复杂类型。且第一项就是元组的键。Theory
所需要的keys
仅取第一项,因为每个键是xunit支持的类型,可以Theory分行显示,而剩余的数据部分,在测试方法中通过mp.[x]
查询Map来获得。