tags:

views:

45

answers:

1

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?

+2  A: 

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.

Mark Seemann
Does this work if MyClass has a dependency on a concrete type instead of an interface?
Marius
Yes, Autofac's `AnyConcreteTypeNotAlreadyRegisteredSource` takes care of resolving concrete types.
Mark Seemann