AutoMockContainer with support for automocking classes with non-interface dependencies
c#, mocking, moq, tdd
Solution
You can pretty easily write one yourself if you leverage a DI Container that supports just-in-time resolution of requested types.
I recently wrote a prototype for exactly that purpose using Autofac and Moq, but other containers could be used instead.
Here's the appropriate IRegistrationSource:
public class AutoMockingRegistrationSource : IRegistrationSource
{
private readonly MockFactory mockFactory;
public AutoMockingRegistrationSource()
{
this.mockFactory = new MockFactory(MockBehavior.Default);
this.mockFactory.CallBase = true;
this.mockFactory.DefaultValue = DefaultValue.Mock;
}
public MockFactory MockFactory
{
get { return this.mockFactory; }
}
#region IRegistrationSource Members
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
if (swt == null)
{
yield break;
}
var existingReg = registrationAccessor(service);
if (existingReg.Any())
{
yield break;
}
var reg = RegistrationBuilder.ForDelegate((c, p) =>
{
var createMethod =
typeof(MockFactory).GetMethod("Create", Type.EmptyTypes).MakeGenericMethod(swt.ServiceType);
return ((Mock)createMethod.Invoke(this.MockFactory, null)).Object;
}).As(swt.ServiceType).CreateRegistration();
yield return reg;
}
#endregion
}
You can now set up the container in a unit test like this:
[TestMethod]
public void ContainerCanCreate()
{
// Fixture setup
var builder = new ContainerBuilder();
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
builder.RegisterSource(new AutoMockingRegistrationSource());
var container = builder.Build();
// Exercise system
var result = container.Resolve<MyClass>();
// Verify outcome
Assert.IsNotNull(result);
// Teardown
}
That's all you need to get started.
MyClass is a concrete class with an abstract dependency. Here is the constructor signature:
public MyClass(ISomeInterface some)
Notice that you don't have to use Autofac (or any other DI Container) in your production code.
Problem
I have a constructor which has a non-interface dependency: ``` public MainWindowViewModel(IWorkItemProvider workItemProvider, WeekNavigatorViewModel weekNavigator) ``` I am using the Moq.Contrib automockcontainer. If I try to automock the MainWindowViewModel class, I get an error due to the WeekNavigatorViewModel dependency. Are there any automocking containers which supports mocking of non-interface types? As Mark has shown below; yes you can! :-) I replaced the Moq.Contrib AutoMockContainer with the stuff Mark presents in his answer, the only difference is that the auto-generated mocks are registered as singletons, but you can make this configurable. Here is the final solution: ``` /// <summary> /// Auto-mocking factory that can create an instance of the /// class under test and automatically inject mocks for all its dependencies. /// </summary> /// <remarks> /// Mocks interface and class dependencies /// </remarks> public class AutoMockContainer { readonly IContainer _container; public AutoMockContainer(MockFactory factory) { var builder = new ContainerBuilder(); builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource()); builder.RegisterSource(new MoqRegistrationSource(factory)); _container = builder.Build(); } /// <summary> /// Gets or creates a mock for the given type, with /// the default behavior specified by the factory. /// </summary> public Mock<T> GetMock<T>() where T : class { return (_container.Resolve<T>() as IMocked<T>).Mock; } /// <summary> /// Creates an instance of a class under test, /// injecting all necessary dependencies as mocks. /// </summary> /// <typeparam name="T">Requested object type.</typeparam> public T Create<T>() where T : class { return _container.Resolve<T>(); } public T Resolve<T>() { return _container.Resolve<T>(); } /// <summary> /// Registers and resolves the given service on the container. /// </summary> /// <typeparam name="TService">Service</typeparam> /// <typeparam name="TImplementation">The implementation of the service.</typeparam> public void Register<TService, TImplementation>() { var builder = new ContainerBuilder(); builder.RegisterType<TImplementation>().As<TService>().SingleInstance(); builder.Update(_container); } /// <summary> /// Registers the given service instance on the container. /// </summary> /// <typeparam name="TService">Service type.</typeparam> /// <param name="instance">Service instance.</param> public void Register<TService>(TService instance) { var builder = new ContainerBuilder(); if (instance.GetType().IsClass) builder.RegisterInstance(instance as object).As<TService>(); else builder.Register(c => instance).As<TService>(); builder.Update(_container); } class MoqRegistrationSource : IRegistrationSource { private readonly MockFactory _factory; private readonly MethodInfo _createMethod; public MoqRegistrationSource(MockFactory factory) { _factory = factory; _createMethod = factory.GetType().GetMethod("Create", new Type[] { }); } public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor) { var swt = service as IServiceWithType; if (swt == null) { yield break; } if (!swt.ServiceType.IsInterface) yield break; var existingReg = registrationAccessor(service); if (existingReg.Any()) { yield break; } var reg = RegistrationBuilder.ForDelegate((c, p) => { var createMethod = _createMethod.MakeGenericMethod(swt.ServiceType); return ((Mock)createMethod.Invoke(_factory, null)).Object; }).As(swt.ServiceType).SingleInstance().CreateRegistration(); yield return reg; } public bool IsAdapterForIndividualComponents { get { return false; } } } } ```