Jef*_*eff 5 c# unit-testing xunit.net autofixture
\n我一直在尝试对一些简单的不可变对象进行建模,其中我有两个构造函数重载:一个重载接受每个只读属性的参数,另一个重载接受其自身的实例以进行克隆(以便在大多数属性值将与另一个实例的相同)。
\n\npublic class Parent {\n public Parent(Child child, Pet pet) {\n Child = child;\n Pet = pet;\n }\n\n public Parent(Parent template) {\n Child = template.Child;\n Pet = template.Pet;\n }\n\n public Child Child { get; private set; }\n public Pet Pet { get; private set; }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n正如我所期望的,这个测试通过了:
\n\n[Theory]\n[AutoData]\npublic void Properties_Equal_Constructor_Arguments(Child expectedChild, Pet expectedPet)\n{\n // act\n var sut = new Parent(expectedChild, expectedPet);\n\n // assert\n sut.Child.Should().Be(expectedChild);\n sut.Pet.Should().Be(expectedPet);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n但是这个测试抛出一个Ploeh.AutoFixture.ObjectCreationException,无法创建类型的实例System.Reflection.RuntimeParameterInfo(使用 AutoData 参数),或类型Ploeh.AutoFixture.Kernel.SeededRequest(不使用 AutoData)(?!?)
[Theory]\n[AutoData]\npublic void Properties_Equal_Properties_Of_Template_Constructor_Argument(Parent template)\n{\n // arrange\n //var fixture = new Fixture(); // yields same error but for type\n //var template = fixture.Create<Parent>();// Ploeh.AutoFixture.Kernel.SeededRequest\n var expectedChild = template.Child;\n var expectedPet = template.Pet;\n\n // act\n var sut = new Parent(template);\n\n // assert\n sut.Child.Should().Be(expectedChild);\n sut.Pet.Should().Be(expectedPet);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n首先,我在我的类中没有发现任何循环引用,就像您通常在有关此问题的问题中看到的那样,其中子对象具有对其父对象的属性引用(就像有时在数据库实体建模中发生的情况一样)。其次,当我过去收到此错误时,它指的是对我的类中明确使用的类型的循环引用,与本例不同。
\n\n为什么会发生这种情况?
\n\n为了完整起见,以下是其他相关类和收到的完整错误消息:
\n\npublic class Child {\n public Child(string value) {\n Value = value;\n }\n public string Value { get; private set; }\n}\n\npublic class Pet {\n public Pet(string name) {\n Name = name;\n }\n public string Name { get; private set; }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\nSystem.InvalidOperationException 获取理论数据时抛出异常\n AutoFixtureTests.CircularReferenceTests.Properties_Equal_Properties_Of_Template_Constructor_Argument:\n Ploeh.AutoFixture.ObjectCreationException: AutoFixture 无法\n 创建 System.Reflection.RuntimeParameterInfo 类型的实例\n 因为遍历的对象图包含循环引用。\n 有关循环路径的信息如下。当 Fixture 配备 ThrowingRecursionBehavior 时,这是正确的行为,这是默认行为。这可确保您了解代码中的循环引用。您的第一反应应该是重新设计您的 API,以消除所有循环引用。但是,如果这是不可能的(很可能是因为部分或全部 API 是由第三方提供的),您可以将此默认行为替换为不同的行为:在 Fixture 实例上,从 Fixture.Behaviors 中删除 ThrowingRecursionBehavior,然后添加 OmitOnRecursionBehavior 实例。路径:\n AutoFixtureTests.Parent 模板 --> AutoFixtureTests.Parent --> \n AutoFixtureTests.Parent 模板 --> \n Ploeh.AutoFixture.Kernel.SeededRequest
\n\nat\n Ploeh.AutoFixture.Kernel.ThrowingRecursionHandler.HandleRecursiveRequest(Object\n request, IEnumerable
\n1 recordedRequests) at\n Ploeh.AutoFixture.Kernel.RecursionGuard.Create(Object request,\n ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\n request, ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\n request, ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.Postprocessor1.Create(Object request,\n ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\n request, ISpecimenContext)上下文)在\n Ploeh.AutoFixture.Kernel.TracingBuilder.Create(对象请求,\n ISpecimenContext 上下文)在\n Ploeh.AutoFixture.Kernel.TerminateWithPathSpecimenBuilder.Create(对象\n 请求,ISpecimenContext 上下文)在\n Ploeh.AutoFixture .Kernel.RecursionGuard.Create(对象请求,\n ISpecimenContext 上下文)位于\n System.Linq.Enumerable.WhereSelectArrayIterator2.MoveNext() at\n System.Collections.Generic.List1..ctor(IEnumerable 1 源)位于\n Ploeh.AutoFixture.Kernel.MethodInvoker.Create(对象请求, \n ISpecimenContext 上下文)位于\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(对象\n 请求,ISpecimenContext 上下文)位于\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(对象\n 请求,ISpecimenContext 上下文)位于\n Ploeh.AutoFixture.Kernel.Postprocessor 1.Create(对象请求,\n ISpecimenContext 上下文) at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(对象\n 请求,ISpecimenContext 上下文) at\n Ploeh.AutoFixture.Kernel.TracingBuilder .Create(对象请求,\n ISpecimenContext 上下文) at\n Ploeh.AutoFixture.Kernel.TerminateWithPathSpecimenBuilder.Create(对象\n 请求,ISpecimenContext 上下文) at\n Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,\n ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\n request, ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\n request, ISpecimenContext context) at\n Ploeh. AutoFixture.Kernel.Postprocessor`1.Create(对象请求,\n ISpecimenContext 上下文)位于\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(对象\n 请求,ISpecimenContext 上下文)位于\n Ploeh.AutoFixture.Kernel.TracingBuilder。创建(对象请求,\n ISpecimenContext 上下文)在\n Ploeh.AutoFixture.Kernel.TerminateWithPathSpecimenBuilder.Create(对象\n 请求,ISpecimenContext 上下文)在\n Ploeh.AutoFixture.Kernel.RecursionGuard.Create(对象请求,\n ISpecimenContext context) at\n Ploeh.AutoFixture.Xunit.AutoDataAttribute.GetData(MethodInfo\n methodUnderTest, Type[]parameterTypes) at\n Xunit.Extensions.TheoryAttribute.d_ 7.MoveNext() at\n Xunit.Extensions.TheoryAttribute.EnumerateTestCommands (IMethodInfo\n 方法) at\n Xunit.Extensions.TheoryAttribute.<>c1 collection)
\n at System.Linq.Enumerable.ToList[TSource](IEnumerable1.Create(Object request,\n ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\n request, ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.TracingBuilder.Create(Object request,\n ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.TerminatingWithPathSpecimenBuilder.Create(Object\n request, ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.RecursionGuard.Create(Object request,\n ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\n request, ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\n request, ISpecimenContext context) at\n Ploeh.AutoFixture.Kernel.Postprocessor_DisplayClass5.b__1()\n at\n Xunit.Extensions.TheoryAttribute.LambdaTestCommand.Execute(Object\xc2\xa0testClass)
当不使用 AutoDataAttribute 时:
\n\n\n\nPloeh.AutoFixture.ObjectCreationException AutoFixture 无法\n 创建 Ploeh.AutoFixture.Kernel.SeededRequest\n 类型的实例,因为遍历的对象图包含循环引用。\n 有关循环路径的信息如下。当 Fixture 配备 ThrowingRecursionBehavior 时,这是正确的行为,这是默认行为。这可确保您了解代码中的循环引用。您的第一反应应该是重新设计您的 API,以消除所有循环引用。但是,如果这是不可能的(很可能是因为部分或全部 API 是由第三方提供的),您可以将此默认行为替换为不同的行为:在 Fixture 实例上,从 Fixture.Behaviors 中删除 ThrowingRecursionBehavior,然后添加 OmitOnRecursionBehavior 实例。路径:\n AutoFixtureTests.Parent 模板 --> AutoFixtureTests.Parent --> \n AutoFixtureTests.Parent 位于\n Ploeh.AutoFixture.Kernel.ThrowingRecursionHandler.HandleRecursiveRequest(Object\xc2\xa0request,\xc2\xa0IEnumerable
\n1\xc2\xa0recordedRequests)\n at\n Ploeh.AutoFixture.Kernel.RecursionGuard.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.Postprocessor1.Create(Object \xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh .AutoFixture.Kernel.TracingBuilder.Create(对象\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.TerminateWithPathSpecimenBuilder.Create(对象\xc2\xa0request,\xc2\xa0ISpecimenContext\ xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.RecursionGuard.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object \xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh .AutoFixture.Kernel.Postprocessor1.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.TracingBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.TerminatingWithPathSpecimenBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.RecursionGuard.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at System.Linq.Enumerable.WhereSelectArrayIterator2.MoveNext() at\n System.Collections.Generic.List1..ctor(IEnumerable1\xc2\xa0collection)
\n at System.Linq.Enumerable.ToList(IEnumerable1\xc2\xa0source) at\n Ploeh.AutoFixture.Kernel.MethodInvoker.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.Postprocessor1.Create(对象\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(对象\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\ n at\n Ploeh.AutoFixture.Kernel.TracingBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.TerminateWithPathSpecimenBuilder.Create(Object\xc2\xa0request, \xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.RecursionGuard.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel。 CompositeSpecimenBuilder.Create(对象\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(对象\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\ n at\n Ploeh.AutoFixture.Kernel.Postprocessor`1.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.CompositeSpecimenBuilder.Create(Object\xc2\ xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.TracingBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture。 Kernel.TerminateWithPathSpecimenBuilder.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context)\n at\n Ploeh.AutoFixture.Kernel.RecursionGuard.Create(Object\xc2\xa0request,\xc2\xa0ISpecimenContext\xc2\xa0context )\n 在\n Ploeh.AutoFixture.SpecimenFactory.Create(ISpecimenContext\xc2\xa0context,\xc2\xa0T\xc2\xa0seed)\n 在 Ploeh.AutoFixture.SpecimenFactory.Create(ISpecimenContext\xc2\xa0context) \n 在\ AutoFixtureTests.cs 中的 AutoFixtureTests.CircularReferenceTests.Properties_Equal_Properties_Of_Template_Constructor_Argument()\n:第 28 行
对于任何试图避免由于实体框架和虚拟成员而导致循环引用的人,这里有一个自定义,可以在生成模拟数据时忽略它们。
public class IgnoreVirtualMembers : ISpecimenBuilder
{
public Type ReflectedType { get; }
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi != null) //// is a property
{
if (this.ReflectedType == null || //// is hosted anywhere
this.ReflectedType == pi.ReflectedType) //// is hosted in defined type
{
if (pi.GetGetMethod().IsVirtual)
{
return new OmitSpecimen();
}
}
}
return new NoSpecimen();
}
}
public class IgnoreVirtualMembersCustomisation : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new IgnoreVirtualMembers());
}
}
Run Code Online (Sandbox Code Playgroud)
随着用法:
new Fixture().Customize(new IgnoreVirtualMembersCustomisation());
Run Code Online (Sandbox Code Playgroud)