Reputation: 1124
Is it possible to reuse production IoC container registration in integration tests when using AutoFixture?
The problem is that I need the following fixture setup to inject mocks if dependency is not registered and inject "real" database related dependencies
var fixture = new Fixture().WithMocks().WithRealDatabase()
The solution I've tried
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);
}
}
But the problem with this approach is that container.Resolve
will not take into account already registered dependencies in AutoFixture.
Is there any alternative to solve this problem to have more complicated registrations?
Upvotes: 3
Views: 1052
Reputation: 233377
The general approach looks sound, but you should add the ContainerSpecimenBuilder
to ResidueCollectors
instead of to Customizations
:
fixture.ResidueCollectors.Add(new ContainerSpecimenBuilder(containerBuilder.Build()));
AutoMoqCustomization
also adds a node to ResidueCollectors
, so you may need to experiment a bit with the specific ordering to figure out exactly how to make it behave like you want it to behave. The ordering matters.
For more information about the difference between Customizations
and ResidueCollectors
, see the AutoFixture architecture documentation.
A slightly simpler (and safer?) implementation of ContainerSpecimenBuilder
could be just handling requests for Type
instances directly, instead of for SeededRequest
, as almost all SeededRequest
values are relayed to requests for Type
objects anyway:
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);
}
}
Upvotes: 2