将复杂的parameter passing给
Xunit有一个很好的特性 :你可以用一个Theory
属性创build一个testing,并将数据放在InlineData
属性中,xUnit将生成很多testing,然后testing它们。
我想有这样的东西,但我的方法的参数不是'简单的数据'(如string
, int
, double
),而是我的类的列表:
public static void WriteReportsToMemoryStream( IEnumerable<MyCustomClass> listReport, MemoryStream ms, StreamWriter writer) { ... }
xxxxData
中有许多xxxxData
属性。 检查例如PropertyData
属性。
您可以实现返回IEnumerable<object[]>
的属性。 这个方法生成的每个object[]
都将被“解压缩”,作为一次调用[Theory]
方法的参数。
另一种select是ClassData
,它的工作原理相同,但是允许在不同类/名称空间的testing之间轻松共享“生成器”,并将“数据生成器”与实际testing方法分开。
从这里看到这些例子 :
PropertyData示例
public class StringTests2 { [Theory, PropertyData(nameof(SplitCountData))] public void SplitCount(string input, int expectedCount) { var actualCount = input.Split(' ').Count(); Assert.Equal(expectedCount, actualCount); } public static IEnumerable<object[]> SplitCountData { get { // Or this could read from a file. :) return new[] { new object[] { "xUnit", 1 }, new object[] { "is fun", 2 }, new object[] { "to test with", 3 } }; } } }
ClassData示例
public class StringTests3 { [Theory, ClassData(typeof(IndexOfData))] public void IndexOf(string input, char letter, int expected) { var actual = input.IndexOf(letter); Assert.Equal(expected, actual); } } public class IndexOfData : IEnumerable<object[]> { private readonly List<object[]> _data = new List<object[]> { new object[] { "hello world", 'w', 6 }, new object[] { "goodnight moon", 'w', -1 } }; public IEnumerator<object[]> GetEnumerator() { return _data.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
要更新@Quetzalcoatl的答案:属性[PropertyData]
已被[MemberData]
取代,它以任何返回IEnumerable<object[]>
静态方法,字段或属性的string名称作为参数。 (我觉得有一个迭代器方法是非常好的,它可以一次一个地计算testing用例,并在计算时产生它们。
枚举器返回的序列中的每个元素都是一个object[]
,每个数组必须是相同的长度,并且长度必须是testing用例的参数数量(用属性[MemberData]
注释,每个元素必须相同键入相应的方法参数(或者也许它们可以是可转换types,我不知道)
(请参阅xUnit.net 2014年3月发行说明以及带有示例代码的实际修补程序 。)
创build匿名对象数组不是最简单的方法来构造数据,所以我在我的项目中使用这种模式
首先定义一些可重用的共享类
//http://stackoverflow.com/questions/22093843 public interface ITheoryDatum { object[] ToParameterArray(); } public abstract class TheoryDatum : ITheoryDatum { public abstract object[] ToParameterArray(); public static ITheoryDatum Factory<TSystemUnderTest, TExecptedOutput>(TSystemUnderTest sut, TExecptedOutput expectedOutput, string description) { var datum= new TheoryDatum<TSystemUnderTest, TExecptedOutput>(); datum.SystemUnderTest = sut; datum.Description = description; datum.ExpectedOutput = expectedOutput; return datum; } } public class TheoryDatum<TSystemUnderTest, TExecptedOutput> : TheoryDatum { public TSystemUnderTest SystemUnderTest { get; set; } public string Description { get; set; } public TExecptedOutput ExpectedOutput { get; set; } public override object[] ToParameterArray() { var output = new object[3]; output[0] = SystemUnderTest; output[1] = ExpectedOutput; output[2] = Description; return output; } }
现在您的个人testing和成员数据更容易编写和清洁。
public class IngredientTests : TestBase { [Theory] [MemberData(nameof(IsValidData))] public void IsValid(Ingredient ingredient, string testDescription, bool expectedResult) { Assert.True(ingredient.IsValid == expectedResult, testDescription); } public static IEnumerable<object[]> IsValidData { get { var food = new Food(); var quantity = new Quantity(); var data= new List<ITheoryDatum>(); data.Add(TheoryDatum.Factory(new Ingredient { Food = food } , false, "Quantity missing")); data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity } , false, "Food missing")); data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity, Food = food } , true, "Valid")); return data.ConvertAll(d => d.ToParameterArray()); } } }
stringDescription
属性是在你的许多testing用例失败时抛出一个骨头
你可以试试这个方法:
public class TestClass { bool isSaturday(DateTime dt) { string day = dt.DayOfWeek.ToString(); return (day == "Saturday"); } [Theory] [MemberData("IsSaturdayIndex", MemberType = typeof(TestCase))] public void test(int i) { // parse test case var input = TestCase.IsSaturdayTestCase[i]; DateTime dt = (DateTime)input[0]; bool expected = (bool)input[1]; // test bool result = isSaturday(dt); result.Should().Be(expected); } }
创build另一个类来保存testing数据:
public class TestCase { public static readonly List<object[]> IsSaturdayTestCase = new List<object[]> { new object[]{new DateTime(2016,1,23),true}, new object[]{new DateTime(2016,1,24),false} }; public static IEnumerable<object[]> IsSaturdayIndex { get { List<object[]> tmp = new List<object[]>(); for (int i = 0; i < IsSaturdayTestCase.Count; i++) tmp.Add(new object[] { i }); return tmp; } } }
我想你在这里弄错了。 xUnit Theory
属性实际上意味着什么:您希望通过发送特定/随机值作为被测函数接收的参数来testing此函数。 这意味着你定义的下一个属性,如: InlineData
, PropertyData
, ClassData
等将成为这些参数的来源。 这意味着你应该构造源对象来提供这些参数。 在你的情况下,我想你应该使用ClassData
对象作为源。 另外请注意, ClassData
inheritance自: IEnumerable<>
– 这意味着每次生成的另一组参数将被用作传入的函数testing参数,直到IEnumerable<>
产生值。
示例: Tom DuPont .NET
示例可能不正确 – 我很久没有使用xUnit了