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

How to setup more complicated (IoC like) registration in AutoFixture

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

问题是,如果依赖项未注册并注入 "real" 数据库相关依赖项,我需要以下夹具设置来注入模拟

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 中注册的依赖项。

是否有任何其他方法可以解决这个问题以进行更复杂的注册?

一般方法看起来不错,但您应该将 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);
    }
}