能否将测试类中的字段作为XUnit Theory的参数使用?
XUnit中能否直接用字段作为Theory的测试数据?
我习惯将测试数据定义为测试类的静态字段,使用XUnit的Theory特性运行多组测试。之前用MemberData特性时,它只支持IEnumerable类型,不得不写繁琐的包装代码:
public class MyTests { public static string s1 = "test"; public static string s2 = "bees"; public static string s3 = "pear"; public static string s4 = "joke"; public static IEnumerable<object[]> Strings => new List<object[]> { new object[] { s1 }, new object[] { s2 }, new object[] { s3 }, new object[] { s4 }, }; [Theory] [MemberData(nameof(Strings))] public void ContainsLetterE(string s) { s.Should().Contain("e"); } }
想知道有没有办法直接引用字段,类似这样的写法:
public class MyTests { public static string s1 = "test"; public static string s2 = "bees"; public static string s3 = "pear"; public static string s4 = "joke"; [Theory] [Field(nameof(s1))] [Field(nameof(s2))] [Field(nameof(s3))] [Field(nameof(s4))] public void ContainsLetterE(string s) { s.Should().Contain("e"); } }
这种方式是否可行,还是必须基于IEnumerable类型实现?
解答
XUnit原生并没有提供[Field]这类直接引用字段的特性,但有几种方案可以避免繁琐的IEnumerable包装:
1. 使用[InlineData]直接传入值
如果测试数据不需要复用,直接用[InlineData]是最简单的方式,不需要额外定义字段:
public class MyTests { [Theory] [InlineData("test")] [InlineData("bees")] [InlineData("pear")] [InlineData("joke")] public void ContainsLetterE(string s) { s.Should().Contain("e"); } }
2. 自定义[FieldData]特性
可以自己实现一个类似需求的特性,通过反射读取指定静态字段的值:
using System; using System.Collections.Generic; using System.Reflection; using Xunit; using Xunit.Sdk; public class FieldDataAttribute : DataAttribute { private readonly string _fieldName; public FieldDataAttribute(string fieldName) { _fieldName = fieldName; } public override IEnumerable<object[]> GetData(MethodInfo testMethod) { var field = testMethod.DeclaringType.GetField(_fieldName, BindingFlags.Public | BindingFlags.Static); if (field == null) throw new ArgumentException($"未找到字段 {_fieldName}"); yield return new[] { field.GetValue(null) }; } }
之后就可以像你期望的那样使用:
public class MyTests { public static string s1 = "test"; public static string s2 = "bees"; public static string s3 = "pear"; public static string s4 = "joke"; [Theory] [FieldData(nameof(s1))] [FieldData(nameof(s2))] [FieldData(nameof(s3))] [FieldData(nameof(s4))] public void ContainsLetterE(string s) { s.Should().Contain("e"); } }
注:这个自定义特性仅处理public static字段,若需要支持其他访问权限的字段,可以修改BindingFlags参数扩展逻辑。
3. 简化MemberData的写法
如果还是想用MemberData,可以用LINQ简化IEnumerable的生成代码,避免手动逐个包装数组:
public class MyTests { public static string s1 = "test"; public static string s2 = "bees"; public static string s3 = "pear"; public static string s4 = "joke"; public static IEnumerable<object[]> Strings => new[] { s1, s2, s3, s4 }.Select(value => new object[] { value }); [Theory] [MemberData(nameof(Strings))] public void ContainsLetterE(string s) { s.Should().Contain("e"); } }
内容的提问来源于stack exchange,提问作者Mr. Boy
相关产品推荐
相关产品推荐

