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

时间:2017-09-27 13:50:16

标签: c# unit-testing asp.net-web-api autofixture

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

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

由于需要进一步定制,我想创建一个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无法执行   创建一个System.RuntimeType类型的实例,因为遍历了   对象图包含循环引用。 [...]

     

路径:Foo.Common.Tests.AutoFixture.SpecimenBuilders.DummyController    - &GT; 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);
    }
}

在这种情况下,使用DummyController类型似乎不再使用SpecimenBuilder。这个空的自定义会使测试通过什么?它是否会覆盖样品制造商?但是为什么不抛出同样的例外,因为我不告诉他省略任何东西(无论如何,我不知道该怎么做才能省略......)?

我想我可以使用OmitOnRecursionBehavior,但是我想保留默认行为以避免在所有其他情况下递归,而且我更愿意了解发生了什么(或者我是否确实笨)。

1 个答案:

答案 0 :(得分:1)

只需删除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通过了初始的Guard子句,var controller = context.Resolve(t) as ApiController; 会进入无限递归:

context.Resolve(t)

ISpecimenBuilder的调用进入新的对象创建'会话'。 AutoFixture会询问其树中的每个t是否可以处理ApiControllerSpecimenBuilder的请求。当它达到context.Resolve(t)时,它会再次致电ApiController进行回复,依此类推。

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

但是,如果我正确理解了整个用例,那么实际的要求是,在AutoFixture为您创建对象之后,您希望对Postprocessor实例进行某种后处理。 / p>

这种情况的一般解决方案是使用Postprocessor<T><br>,但有时可能会涉及到正确。通常,有更简单的方法可以实现您想要做的事情。

如果您需要帮助,请提出其他问题。您下次不需要提供赏金,因为我通常会监控 autofixture 标记。这个问题不知何故引起了我的注意;对不起!