C# AutoFixture混合属性数据,具有多个条目和AutoData(使用AutoMoqCustomization)
我已经研究了这两个类似的问题:C# AutoFixture混合属性数据,具有多个条目和AutoData(使用AutoMoqCustomization),c#,moq,xunit,xunit.net,autofixture,C#,Moq,Xunit,Xunit.net,Autofixture,我已经研究了这两个类似的问题: 他们很棒,让我快到了。但这两个示例在发出的IEnumerable PropertyData中只使用了一个条目(即:yield return new object[]{2,4};--see:)这是可行的,但每当我想对多个object[]测试数据进行测试时,它就会崩溃。我想发送一整套测试数据 我认为这里的答案()与我需要的答案相似,但我想不出来。我基本上需要AutoFixture为PropertyData的每个迭代创建一个sut实例 一些参考: public
yield return new object[]{2,4};
--see:)这是可行的,但每当我想对多个object[]测试数据进行测试时,它就会崩溃。我想发送一整套测试数据
我认为这里的答案()与我需要的答案相似,但我想不出来。我基本上需要AutoFixture为PropertyData的每个迭代创建一个sut
实例
一些参考:
public static IEnumerable<object[]> TestData
{
get
{
// totally doesn't work
return new List<object[]>()
{
new object[] { new MsgData() { Code = "1" }, CustomEnum.Value1 },
new object[] { new MsgData() { Code = "2" }, CustomEnum.Value2 },
new object[] { new MsgData() { Code = "3" }, CustomEnum.Value3 },
new object[] { new MsgData() { Code = "4" }, CustomEnum.Value4 },
};
// totally works
//yield return new object[] { new MsgData() { Code = "1" }, CustomEnum.Value1 };
}
}
AutoMoqPropertyData
实现:
public class AutoMoqPropertyDataAttribute : CompositeDataAttribute
{
public AutoMoqPropertyDataAttribute(string dataProperty)
: base(new DataAttribute[]
{
new PropertyDataAttribute(dataProperty),
new AutoDataAttribute(new Fixture().Customize(new AutoMoqCustomization()))
})
{ }
}
我错过了什么?当需要PropertyData数据的多次迭代时,我可以像这样混合PropertyData和AutoData驱动的AutoFixture属性吗
编辑
以下是异常堆栈跟踪:
System.InvalidOperationException: Expected 3 parameters, got 2 parameters
at Ploeh.AutoFixture.Xunit.CompositeDataAttribute.<GetData>d__0.MoveNext()
at Xunit.Extensions.TheoryAttribute.<GetData>d__7.MoveNext()
at Xunit.Extensions.TheoryAttribute.EnumerateTestCommands(IMethodInfo method)
Result StackTrace:
at Xunit.Extensions.TheoryAttribute.<>c__DisplayClass5.<EnumerateTestCommands>b__1()
at Xunit.Extensions.TheoryAttribute.LambdaTestCommand.Execute(Object testClass)
System.InvalidOperationException:应为3个参数,得到2个参数
在Ploeh.AutoFixture.Xunit.CompositeDataAttribute.d__0.MoveNext()中
在Xunit.Extensions.TheoryAttribute.d_u7.MoveNext()中
在Xunit.Extensions.TheoryAttribute.EnumerateTestCommands(IMethodInfo方法)中
结果跟踪:
在Xunit.Extensions.TheoryAttribute.c__DisplayClass5.b__1()中
在Xunit.Extensions.TheoryAttribute.LambdaTestCommand.Execute(对象测试类)
您必须提供该链接中描述的测试用例
以下是用于通过测试的类型:
type MsgData = { Code : string }
[<AutoOpen>]
type Custom = Value1 | Value2 | Value3 | Value4
type SomeObject () =
member this.GetEnum msgData =
match msgData.Code with
| "1" -> Some(Value1)
| "2" -> Some(Value2)
| "3" -> Some(Value3)
| "4" -> Some(Value4)
| _ -> None
[<AttributeUsage(AttributeTargets.Field, AllowMultiple = true)>]
type AutoMoqPropertyDataAttribute (dataProperty) =
inherit CompositeDataAttribute(
PropertyDataAttribute(dataProperty),
AutoDataAttribute())
type MsgData={code:string}
[]
类型自定义=值1 |值2 |值3 |值4
键入SomeObject()=
成员this.GetEnum msgData=
将msgData.Code与匹配
|“1”->部分(值1)
|“2”->部分(值2)
|“3”->部分(值3)
|“4”->部分(值4)
|无
[]
类型AutoMoqPropertyDataAttribute(dataProperty)=
继承复合属性(
PropertyDataAttribute(数据属性),
AutoDataAttribute())
我自己也需要它,我编写了一个新类PropertyAutoData
,它结合了PropertyData
和AutoFixture,就像InlineAutoData
结合了InlineData
和AutoFixture一样。用法是:
[Theory]
[PropertyAutoData("ColorPairs")]
public void ReverseColors([TestCaseParameter] TestData testData, int autoGenValue) { ... }
public static IEnumerable<object[]> ColorPairs
{
get
{
yield return new object[] { new TestData { Input = Color.Black, Expected = Color.White } };
yield return new object[] { new TestData { Input = Color.White, Expected = Color.Black } };
}
}
您还可以将其与SubSec的论文一起使用:
[Thesis]
[PropertyAutoData("ColorPairs")]
public void ReverseColors([TestCaseParameter] TestData testData, int autoGenValue)
要将其用于最小起订量,您需要扩展它,即
public class PropertyMockAutoDataAttribute : PropertyAutoDataAttribute
{
public PropertyFakeAutoDataAttribute(string propertyName)
: base(propertyName, new Fixture().Customize(new AutoMoqCustomization()))
{
}
}
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Ploeh.AutoFixture.Xunit;
using Xunit.Extensions;
/// <summary>
/// Provides a data source for a data theory, with the data coming from a public static property on the test class combined with auto-generated data specimens generated by AutoFixture.
/// </summary>
public class PropertyAutoDataAttribute : AutoDataAttribute
{
private readonly string _propertyName;
public PropertyAutoDataAttribute(string propertyName)
{
_propertyName = propertyName;
}
public PropertyAutoDataAttribute(string propertyName, IFixture fixture)
: base(fixture)
{
_propertyName = propertyName;
}
/// <summary>
/// Gets or sets the scope of auto-generated data.
/// </summary>
public AutoDataScope Scope { get; set; }
public override IEnumerable<object[]> GetData(MethodInfo methodUnderTest, Type[] parameterTypes)
{
var parameters = methodUnderTest.GetParameters();
var testCaseParametersIndices = GetTestCaseParameterIndices(parameters);
if (!testCaseParametersIndices.Any())
{
throw new InvalidOperationException(string.Format("There are no parameters marked using {0}.", typeof(TestCaseParameterAttribute).Name));
}
if (testCaseParametersIndices.Length == parameters.Length)
{
throw new InvalidOperationException(string.Format("All parameters are provided by the property. Do not use {0} unless there are other parameters that AutoFixture should provide.", typeof(PropertyDataAttribute).Name));
}
// 'split' the method under test in 2 methods: one to get the test case data sets and another one to get the auto-generated data set
var testCaseParameterTypes = parameterTypes.Where((t, i) => testCaseParametersIndices.Contains(i)).ToArray();
var testCaseMethod = CreateDynamicMethod(methodUnderTest.Name + "_TestCase", testCaseParameterTypes);
var autoFixtureParameterTypes = parameterTypes.Where((t, i) => !testCaseParametersIndices.Contains(i)).ToArray();
var autoFixtureTestMethod = CreateDynamicMethod(methodUnderTest.Name + "_AutoFixture", autoFixtureParameterTypes);
// merge the test case data and the auto-generated data into a new array and yield it
// the merge depends on the Scope:
// * if the scope is TestCase then auto-generate data once for all tests
// * if the scope is Test then auto-generate data for every test
var testCaseDataSets = GetTestCaseDataSets(methodUnderTest.DeclaringType, testCaseMethod, testCaseParameterTypes);
object[] autoGeneratedDataSet = null;
if (Scope == AutoDataScope.TestCase)
{
autoGeneratedDataSet = GetAutoGeneratedData(autoFixtureTestMethod, autoFixtureParameterTypes);
}
var autoFixtureParameterIndices = Enumerable.Range(0, parameters.Length).Except(testCaseParametersIndices).ToArray();
foreach (var testCaseDataSet in testCaseDataSets)
{
if (testCaseDataSet.Length != testCaseParameterTypes.Length)
{
throw new ApplicationException("There is a mismatch between the values generated by the property and the test case parameters.");
}
var mergedDataSet = new object[parameters.Length];
CopyAtIndices(testCaseDataSet, mergedDataSet, testCaseParametersIndices);
if (Scope == AutoDataScope.Test)
{
autoGeneratedDataSet = GetAutoGeneratedData(autoFixtureTestMethod, autoFixtureParameterTypes);
}
CopyAtIndices(autoGeneratedDataSet, mergedDataSet, autoFixtureParameterIndices);
yield return mergedDataSet;
}
}
private static int[] GetTestCaseParameterIndices(ParameterInfo[] parameters)
{
var testCaseParametersIndices = new List<int>();
for (var index = 0; index < parameters.Length; index++)
{
var parameter = parameters[index];
var isTestCaseParameter = parameter.GetCustomAttributes(typeof(TestCaseParameterAttribute), false).Length > 0;
if (isTestCaseParameter)
{
testCaseParametersIndices.Add(index);
}
}
return testCaseParametersIndices.ToArray();
}
private static MethodInfo CreateDynamicMethod(string name, Type[] parameterTypes)
{
var method = new DynamicMethod(name, typeof(void), parameterTypes);
return method.GetBaseDefinition();
}
private object[] GetAutoGeneratedData(MethodInfo method, Type[] parameterTypes)
{
var autoDataSets = base.GetData(method, parameterTypes).ToArray();
if (autoDataSets == null || autoDataSets.Length == 0)
{
throw new ApplicationException("There was no data automatically generated by AutoFixture");
}
if (autoDataSets.Length != 1)
{
throw new ApplicationException("Multiple sets of data were automatically generated. Only one was expected.");
}
return autoDataSets.Single();
}
private IEnumerable<object[]> GetTestCaseDataSets(Type testClassType, MethodInfo method, Type[] parameterTypes)
{
var attribute = new PropertyDataAttribute(_propertyName) { PropertyType = testClassType };
return attribute.GetData(method, parameterTypes);
}
private static void CopyAtIndices(object[] source, object[] target, int[] indices)
{
var sourceIndex = 0;
foreach (var index in indices)
{
target[index] = source[sourceIndex++];
}
}
}
/// <summary>
/// Defines the scope of auto-generated data in a theory.
/// </summary>
public enum AutoDataScope
{
/// <summary>
/// Data is auto-generated only once for all tests.
/// </summary>
TestCase,
/// <summary>
/// Data is auto-generated for every test.
/// </summary>
Test
}
/// <summary>
/// Indicates that the parameter is part of a test case rather than being auto-generated by AutoFixture.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public class TestCaseParameterAttribute : Attribute
{
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
运用系统反思;
使用System.Reflection.Emit;
使用Ploeh.AutoFixture.Xunit;
使用Xunit.Extensions;
///
///为数据理论提供数据源,数据来自测试类上的公共静态属性,并与AutoFixture生成的自动生成数据样本相结合。
///
公共类PropertyAutoDataAttribute:AutoDataAttribute
{
私有只读字符串_propertyName;
公共属性自动数据属性(字符串propertyName)
{
_propertyName=propertyName;
}
公共PropertyAutoDataAttribute(字符串propertyName,iTexture fixture)
:底座(固定装置)
{
_propertyName=propertyName;
}
///
///获取或设置自动生成数据的范围。
///
公共AutoDataScope作用域{get;set;}
公共重写IEnumerable GetData(MethodInfo methodUnderTest,类型[]参数类型)
{
var parameters=methodUnderTest.GetParameters();
var testCaseParametersIndices=GetTestCaseParameterIndices(参数);
如果(!testCaseParametersIndices.Any())
{
抛出新的InvalidOperationException(string.Format(“没有使用{0}.”标记的参数,typeof(TestCaseParameterAttribute.Name));
}
if(testCaseParametersIndices.Length==parameters.Length)
{
抛出新的InvalidOperationException(string.Format(“所有参数都由属性提供。除非AutoFixture应该提供其他参数,否则不要使用{0}”),,typeof(PropertyDataAttribute.Name));
}
//将测试中的方法“拆分”为两种方法:一种用于获取测试用例数据集,另一种用于获取自动生成的数据集
var testCaseParameterTypes=parameterTypes.Where((t,i)=>testCaseParametersIndices.Contains(i)).ToArray();
var testCaseMethod=CreateDynamicMethod(methodUnderTest.Name+“_TestCase”,testCaseParameterTypes);
var autoFixtureParameterTypes=parameterTypes.Where((t,i)=>!testCaseParametersIndices.Contains(i)).ToArray();
var autoFixtureTestMethod=CreateDynamicMethod(methodUnderTest.Name+“_AutoFixture”,autoFixtureParameterTypes);
//将测试用例数据和自动生成的数据合并到一个新数组中并生成它
//合并取决于范围:
//*如果范围是TestCase,则自动为所有测试生成一次数据
//*如果范围为测试,则自动为每个测试生成数据
var testCaseDataSets=GetTestCaseDataSets(methodUnderTest.DeclaringType、testCaseMethod、testCaseParameterTypes);
对象[]自动生成的数据集=null;
if(Scope==AutoDataScope.TestCase)
{
autoGeneratedDataSet=GetAutoGeneratedData(autoFixtureTestMethod,autoFixtureParameterTypes);
}
var autoFixtureParameterIndices=Enumerable.Range(0,parameters.Length).Except(testCaseParametersIndices.ToArray();
foreach(testCaseDataSets中的var testCaseDataSet)
{
if(testCaseDataSet.Length!=testCaseParameterTypes.Length)
{
抛出新的ApplicationException(“属性生成的值与测试用例参数不匹配”);
}
var mergedDataSet=新对象[parameters.Length];
复制数据集(testCaseDataSet、mergedDataSet、testCaseParametersIndices);
[PropertyAutoData("ColorPairs", Scope = AutoDataScope.Test)] // default is TestCase
[Thesis]
[PropertyAutoData("ColorPairs")]
public void ReverseColors([TestCaseParameter] TestData testData, int autoGenValue)
public class PropertyMockAutoDataAttribute : PropertyAutoDataAttribute
{
public PropertyFakeAutoDataAttribute(string propertyName)
: base(propertyName, new Fixture().Customize(new AutoMoqCustomization()))
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Ploeh.AutoFixture.Xunit;
using Xunit.Extensions;
/// <summary>
/// Provides a data source for a data theory, with the data coming from a public static property on the test class combined with auto-generated data specimens generated by AutoFixture.
/// </summary>
public class PropertyAutoDataAttribute : AutoDataAttribute
{
private readonly string _propertyName;
public PropertyAutoDataAttribute(string propertyName)
{
_propertyName = propertyName;
}
public PropertyAutoDataAttribute(string propertyName, IFixture fixture)
: base(fixture)
{
_propertyName = propertyName;
}
/// <summary>
/// Gets or sets the scope of auto-generated data.
/// </summary>
public AutoDataScope Scope { get; set; }
public override IEnumerable<object[]> GetData(MethodInfo methodUnderTest, Type[] parameterTypes)
{
var parameters = methodUnderTest.GetParameters();
var testCaseParametersIndices = GetTestCaseParameterIndices(parameters);
if (!testCaseParametersIndices.Any())
{
throw new InvalidOperationException(string.Format("There are no parameters marked using {0}.", typeof(TestCaseParameterAttribute).Name));
}
if (testCaseParametersIndices.Length == parameters.Length)
{
throw new InvalidOperationException(string.Format("All parameters are provided by the property. Do not use {0} unless there are other parameters that AutoFixture should provide.", typeof(PropertyDataAttribute).Name));
}
// 'split' the method under test in 2 methods: one to get the test case data sets and another one to get the auto-generated data set
var testCaseParameterTypes = parameterTypes.Where((t, i) => testCaseParametersIndices.Contains(i)).ToArray();
var testCaseMethod = CreateDynamicMethod(methodUnderTest.Name + "_TestCase", testCaseParameterTypes);
var autoFixtureParameterTypes = parameterTypes.Where((t, i) => !testCaseParametersIndices.Contains(i)).ToArray();
var autoFixtureTestMethod = CreateDynamicMethod(methodUnderTest.Name + "_AutoFixture", autoFixtureParameterTypes);
// merge the test case data and the auto-generated data into a new array and yield it
// the merge depends on the Scope:
// * if the scope is TestCase then auto-generate data once for all tests
// * if the scope is Test then auto-generate data for every test
var testCaseDataSets = GetTestCaseDataSets(methodUnderTest.DeclaringType, testCaseMethod, testCaseParameterTypes);
object[] autoGeneratedDataSet = null;
if (Scope == AutoDataScope.TestCase)
{
autoGeneratedDataSet = GetAutoGeneratedData(autoFixtureTestMethod, autoFixtureParameterTypes);
}
var autoFixtureParameterIndices = Enumerable.Range(0, parameters.Length).Except(testCaseParametersIndices).ToArray();
foreach (var testCaseDataSet in testCaseDataSets)
{
if (testCaseDataSet.Length != testCaseParameterTypes.Length)
{
throw new ApplicationException("There is a mismatch between the values generated by the property and the test case parameters.");
}
var mergedDataSet = new object[parameters.Length];
CopyAtIndices(testCaseDataSet, mergedDataSet, testCaseParametersIndices);
if (Scope == AutoDataScope.Test)
{
autoGeneratedDataSet = GetAutoGeneratedData(autoFixtureTestMethod, autoFixtureParameterTypes);
}
CopyAtIndices(autoGeneratedDataSet, mergedDataSet, autoFixtureParameterIndices);
yield return mergedDataSet;
}
}
private static int[] GetTestCaseParameterIndices(ParameterInfo[] parameters)
{
var testCaseParametersIndices = new List<int>();
for (var index = 0; index < parameters.Length; index++)
{
var parameter = parameters[index];
var isTestCaseParameter = parameter.GetCustomAttributes(typeof(TestCaseParameterAttribute), false).Length > 0;
if (isTestCaseParameter)
{
testCaseParametersIndices.Add(index);
}
}
return testCaseParametersIndices.ToArray();
}
private static MethodInfo CreateDynamicMethod(string name, Type[] parameterTypes)
{
var method = new DynamicMethod(name, typeof(void), parameterTypes);
return method.GetBaseDefinition();
}
private object[] GetAutoGeneratedData(MethodInfo method, Type[] parameterTypes)
{
var autoDataSets = base.GetData(method, parameterTypes).ToArray();
if (autoDataSets == null || autoDataSets.Length == 0)
{
throw new ApplicationException("There was no data automatically generated by AutoFixture");
}
if (autoDataSets.Length != 1)
{
throw new ApplicationException("Multiple sets of data were automatically generated. Only one was expected.");
}
return autoDataSets.Single();
}
private IEnumerable<object[]> GetTestCaseDataSets(Type testClassType, MethodInfo method, Type[] parameterTypes)
{
var attribute = new PropertyDataAttribute(_propertyName) { PropertyType = testClassType };
return attribute.GetData(method, parameterTypes);
}
private static void CopyAtIndices(object[] source, object[] target, int[] indices)
{
var sourceIndex = 0;
foreach (var index in indices)
{
target[index] = source[sourceIndex++];
}
}
}
/// <summary>
/// Defines the scope of auto-generated data in a theory.
/// </summary>
public enum AutoDataScope
{
/// <summary>
/// Data is auto-generated only once for all tests.
/// </summary>
TestCase,
/// <summary>
/// Data is auto-generated for every test.
/// </summary>
Test
}
/// <summary>
/// Indicates that the parameter is part of a test case rather than being auto-generated by AutoFixture.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public class TestCaseParameterAttribute : Attribute
{
}