AutoMockContainer 支持具有非接口依赖项的自动模拟类

AutoMockContainer with support for automocking classes with non-interface dependencies(AutoMockContainer 支持具有非接口依赖项的自动模拟类)
本文介绍了AutoMockContainer 支持具有非接口依赖项的自动模拟类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

我有一个非接口依赖的构造函数:

I have a constructor which has a non-interface dependency:

public MainWindowViewModel(IWorkItemProvider workItemProvider, WeekNavigatorViewModel weekNavigator)

我正在使用 Moq.Contrib 自动模拟容器.如果我尝试自动模拟 MainWindowViewModel 类,则会由于 WeekNavigatorViewModel 依赖性而出现错误.

I am using the Moq.Contrib automockcontainer. If I try to automock the MainWindowViewModel class, I get an error due to the WeekNavigatorViewModel dependency.

是否有任何支持模拟非接口类型的自动模拟容器?

如下所示;是的你可以!:-) 我用 Mark 在他的回答中提出的东西替换了 Moq.Contrib AutoMockContainer,唯一的区别是自动生成的模拟被注册为单例,但你可以使它可配置.这是最终的解决方案:

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; }
        }
    }
}

推荐答案

如果您利用支持即时解析请求类型的 DI 容器,您可以非常轻松地自己编写一个.

You can pretty easily write one yourself if you leverage a DI Container that supports just-in-time resolution of requested types.

我最近使用 Autofac 和 Moq 编写了一个原型,但也使用了其他容器可以改用.

I recently wrote a prototype for exactly that purpose using Autofac and Moq, but other containers could be used instead.

这是适当的 IRegistrationSource:

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 是一个具有抽象依赖的具体类.这是构造函数签名:

MyClass is a concrete class with an abstract dependency. Here is the constructor signature:

public MyClass(ISomeInterface some)

请注意,您不必在生产代码中使用 Autofac(或任何其他 DI 容器).

Notice that you don't have to use Autofac (or any other DI Container) in your production code.

这篇关于AutoMockContainer 支持具有非接口依赖项的自动模拟类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

本站部分内容来源互联网,如果有图片或者内容侵犯了您的权益,请联系我们,我们会在确认后第一时间进行删除!

相关文档推荐

How to MOQ an Indexed property(如何最小起订量索引属性)
Mocking generic methods in Moq without specifying T(在 Moq 中模拟泛型方法而不指定 T)
How Moles Isolation framework is implemented?(Moles Isolation 框架是如何实现的?)
Difference between Dependency Injection and Mocking Framework (Ninject vs RhinoMocks or Moq)(依赖注入和模拟框架之间的区别(Ninject vs RhinoMocks 或 Moq))
How to mock Controller.User using moq(如何使用 moq 模拟 Controller.User)
How do I mock a class without an interface?(如何模拟没有接口的类?)