如何在AutoFixture中设置更复杂(IoC类似)的注册

时间:2015-07-31 07:07:52

标签: c# unit-testing autofixture

使用AutoFixture时,是否可以在集成测试中重用生产IoC容器注册?

问题是如果没有注册依赖项并且注入“真正的”数据库相关依赖项,我需要以下fixture设置来注入模拟

var fixture = new Fixture().WithMocks().WithRealDatabase()

我尝试过的解决方案

internal static Fixture WithMocks(this Fixture fixture)
{
    fixture.Customize(new AutoMoqCustomization());
}

internal static Fixture WithRealDatabase(this Fixture fixture)
{
    var containerBuilder = new Autofac.ContainerBuilder();
    ...
    containerBuilder.Register(c => c.Resolve<ISessionFactory>().OpenSession())
    containerBuilder.RegisterGeneric(typeof(Repository<>)).AsImplementedInterfaces()        
    containerBuilder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
        .Where(t => t.Name.EndsWith("Repository"))
        .AsImplementedInterfaces();
    ...
    fixture.Customizations.Add(new ContainerSpecimenBuilder(containerBuilder.Build()));
}

internal class ContainerSpecimenBuilder : ISpecimenBuilder
{
    private readonly IContainer container;

    public ContainerSpecimenBuilder(IContainer container)
    {
        this.container = container;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var seededRequest = request as SeededRequest;

        if (seededRequest == null)
        {
            return new NoSpecimen(request);
        }

        var result = this.container.ResolveOptional(seededRequest.Request as Type);
        return result ?? new NoSpecimen(request);
    }
}

但是这种方法的问题是container.Resolve不会考虑AutoFixture中已注册的依赖项。

是否有其他方法可以解决此问题,以便进行更复杂的注册?

1 个答案:

答案 0 :(得分:2)

一般方法看起来很合理,但您应该将ContainerSpecimenBuilder添加到ResidueCollectors而不是Customizations

fixture.ResidueCollectors.Add(new ContainerSpecimenBuilder(containerBuilder.Build()));

AutoMoqCustomization还向ResidueCollectors添加了一个节点,因此您可能需要对特定排序进行一些实验,以确切了解如何使其表现得像您希望它的行为。 The ordering matters

有关CustomizationsResidueCollectors之间差异的详细信息,请参阅AutoFixture architecture documentation

ContainerSpecimenBuilder的稍微简单(且更安全?)的实现可能只是直接处理Type个实例的请求,而不是SeededRequest,因为几乎所有SeededRequest值无论如何都转发到Type个对象的请求:

internal class ContainerSpecimenBuilder : ISpecimenBuilder
{
    private readonly IContainer container;

    public ContainerSpecimenBuilder(IContainer container)
    {
        this.container = container;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var t = request as Type;

        if (t == null)
            return new NoSpecimen(request);

        var result = this.container.ResolveOptional(t);
        return result ?? new NoSpecimen(request);
    }
}