将复杂的parameter passing给

Xunit有一个很好的特性 :你可以用一个Theory属性创build一个testing,并将数据放在InlineData属性中,xUnit将生成很多testing,然后testing它们。

我想有这样的东西,但我的方法的参数不是'简单的数据'(如stringintdouble ),而是我的类的列表:

 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此函数。 这意味着你定义的下一个属性,如: InlineDataPropertyDataClassData等将成为这些参数的来源。 这意味着你应该构造源对象来提供这些参数。 在你的情况下,我想你应该使用ClassData对象作为源。 另外请注意, ClassDatainheritance自: IEnumerable<> – 这意味着每次生成的另一组参数将被用作传入的函数testing参数,直到IEnumerable<>产生值。

示例: Tom DuPont .NET

示例可能不正确 – 我很久没有使用xUnit了