Autofixture ObjectCreationException - 循环引用:RuntimeParameterInfo 或 Ploeh.AutoFixture.Kernel.SeededRequest

Jef*_*eff 5 c# unit-testing xunit.net autofixture

\n我一直在尝试对一些简单的不可变对象进行建模,其中我有两个构造函数重载:一个重载接受每个只读属性的参数,另一个重载接受其自身的实例以进行克隆(以便在大多数属性值将与另一个实例的相同)。

\n\n
public 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}\n
Run 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}\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是这个测试抛出一个Ploeh.AutoFixture.ObjectCreationException,无法创建类型的实例System.Reflection.RuntimeParameterInfo(使用 AutoData 参数),或类型Ploeh.AutoFixture.Kernel.SeededRequest(不使用 AutoData)(?!?)

\n\n
[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}\n
Run Code Online (Sandbox Code Playgroud)\n\n

首先,我在我的类中没有发现任何循环引用,就像您通常在有关此问题的问题中看到的那样,其中子对象具有对其父对象的属性引用(就像有时在数据库实体建模中发生的情况一样)。其次,当我过去收到此错误时,它指的是对我的类中明确使用的类型的循环引用,与本例不同。

\n\n

为什么会发生这种情况?

\n\n

为了完整起见,以下是其他相关类和收到的完整错误消息:

\n\n
public 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}\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

System.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\n

at\n Ploeh.AutoFixture.Kernel.ThrowingRecursionHandler.HandleRecursiveRequest(Object\n request, IEnumerable 1 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.WhereSelectArrayIterator 2.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](IEnumerable
1.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)

\n
\n\n

当不使用 AutoDataAttribute 时:

\n\n
\n

Ploeh.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 1\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.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.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.List 1..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 行

\n
\n

Val*_*lan 1

对于任何试图避免由于实体框架和虚拟成员而导致循环引用的人,这里有一个自定义,可以在生成模拟数据时忽略它们。

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)