为什么在使用样本生成器时会得到循环引用 DummyApiController --> DummyApiController?

Why do I get a circular reference DummyApiController --> DummyApiController when using a specimen builder?

我正在尝试使用 AutoFixture (3.50.6) 生成 ApiController (WebAPI 2) 的一些子类。

我自定义了 AF 以允许使用 this customization 生成 ApiController。

由于进一步的定制需求,我想创建一个 SpecimenBuilder 来创建任何类型的 ApiController 并使用简单的

应用此配置
fixture.Create<DummyController>();

我试过这个测试 (NUnit 3) :

[TestFixture]
public class ApiControllerSpecimenBuilderTests
{
    [Test]
    public void ShouldCreateAControllerUsingSpecimenBuilder()
    {
        var fixture = new Fixture()
            .Customize(new AutoMoqCustomization())
            .Customize(new ApiControllerCustomization());
        fixture.Customizations.Add(new ApiControllerSpecimenBuilder());

        var ctl = fixture.Create<DummyController>();
    }
}

public class ApiControllerCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Inject(new UriScheme("http"));
        fixture.Customize<HttpConfiguration>(c => c
            .OmitAutoProperties());
        fixture.Customize<HttpRequestMessage>(c => c
            .Do(x =>
                x.Properties.Add(
                    HttpPropertyKeys.HttpConfigurationKey,
                    fixture.Create<HttpConfiguration>())));
        fixture.Customize<HttpRequestContext>(c => c
            .Without(x => x.ClientCertificate));
    }
}

public class ApiControllerSpecimenBuilder : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var t = request as Type;
        if (t == null || !typeof(ApiController).IsAssignableFrom(t))
        {
            return new NoSpecimen();
        }

        var controller = context.Resolve(t) as ApiController;

        // ...

        return controller;
    }
}

public class DummyController : ApiController
{

}

失败并出现以下错误:

Ploeh.AutoFixture.ObjectCreationException : AutoFixture was unable to create an instance of type System.RuntimeType because the traversed object graph contains a circular reference. [...]

Path: Foo.Common.Tests.AutoFixture.SpecimenBuilders.DummyController --> Foo.Common.Tests.AutoFixture.SpecimenBuilders.DummyController

为什么 DummyController 有对其自身类型的引用?

此外,如果我用 DummyController 的空自定义更改测试,它会通过:

[Test]
public void ShouldCreateAControllerUsingSpecimenBuilder()
{
    var fixture = new Fixture()
        .Customize(new AutoMoqCustomization())
        .Customize(new ApiControllerCustomization())
        .Customize(new DummyControllerCustomization()); // new customization
    fixture.Customizations.Add(new ApiControllerSpecimenBuilder());

    var ctl = fixture.Create<DummyController>();
}

public class DummyControllerCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<DummyController>(c => c);
    }
}

在这种情况下,SpecimenBuilder 似乎不再受 DummyController 类型的影响。这个空的定制做了什么使测试通过?它会覆盖样本生成器吗?但是为什么它不抛出同样的异常,因为我没有告诉他省略任何东西(无论如何,我不知道要省略什么...)?

我想我可以使用 OmitOnRecursionBehavior,但我想保留默认行为以避免在所有其他情况下递归,而且我更愿意了解正在发生的事情(或者如果我这样做了smth 真的很愚蠢)。

只需删除 ApiControllerSpecimenBuilder:

[TestFixture]
public class ApiControllerSpecimenBuilderTests
{
    [Test]
    public void ShouldCreateAControllerUsingSpecimenBuilder()
    {
        var fixture = new Fixture()
            .Customize(new AutoMoqCustomization())
            .Customize(new ApiControllerCustomization());
        //fixture.Customizations.Add(new ApiControllerSpecimenBuilder());

        var ctl = fixture.Create<DummyController>();
    }
}

以上版本的测试通过(在我的机器上)。

问题是ApiControllerSpecimenBuilder如果通过初始保护子句就进入无限递归:

var controller = context.Resolve(t) as ApiController;

调用context.Resolve(t)进入新对象创建'session'。 AutoFixture 询问其树中的每个 ISpecimenBuilder 是否可以处理 t 的请求。当它到达 ApiControllerSpecimenBuilder 时,它会再次调用 context.Resolve(t) 进行响应,如此循环往复。

您不需要自己做任何这些工作,因为 AutoFixture 已经完全能够为您创建 ApiController 个实例(只要 ApiControllerCustomization 到位)。

如果我正确理解了整个用例,但是,实际要求是您希望在创建 AutoFixture 后对 ApiController 实例进行某种 post 处理适合你的对象。

这种情况的一般解决方案是使用 PostprocessorPostprocessor<T>,但有时可能需要一些时间才能正确处理。通常,有更简单的方法可以实现您想要做的事情。

如果您需要这方面的帮助,请提出另一个问题。下次你不需要悬赏,因为我通常会监控 autofixture 标签。这个问题不知何故没有引起我的注意。抱歉!