使用mock (Moq)对象更改Autofac模块中的注入

本文关键字:模块 Autofac 注入 mock Moq 对象 使用 | 更新日期: 2023-09-27 18:02:46

我有一个Autofac模块,如下图

    public class ServiceInjector:Module
    {
        protected override void Load(ContainerBuilder builder)
            {
                // many registrations and type looking up here
                ...
                // One of the registration, say t which is found
                // in above looking, is a resource consuming type
                builder.RegisterType(t).As<ITimeConsume>();
                // ...
            }
    }

这个模块在ServiceClass中使用:

public class ServiceClass
{
    static IContainer _ioc;
    public ServiceClass()
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule<ServiceInjector>();
        _ioc = builder.Build();     
    }
    public void InvokeService()
    {
        using(var scope = _ioc.BeginLifetimeScope())
        {
            ITimeConsume obj = scope.Resolve<ITimeConsume>(...);
            var result = obj.DoTimeConsumingJob(...);
            // do something about result here ...
        }    
    }
}

我的问题是:我如何通过mock (Moq) ITimeConsume类测试ServiceClass ?这里我试着在下面写一个测试:

public void Test()
{
    Mock<ITimeConsume> moc = GetMockObj(...);
    // How can I inject moc.Object into ServiceInjector module,
    // so that ServiceClass can use this mock object ?
}

如果这是不可能的方式,有什么更好的设计来模拟耗时的类,也可以注入?

* *

更新:

* *谢谢@dubs和@OldFox提示。我认为关键是Autofac注入器应该在外部初始化,而不是内部控制。所以我利用了Autofac的"On Fly"构建能力。使用LifeTime作用域参数设计ServiceClass构造函数。通过这种设计,我可以在单元测试中注册任何服务,示例如下:

using(var scope = Ioc.BeginLifetimeScope(
    builder =>  builder.RegisterInstance(mockObject).As<ITimeConsume>())

使用mock (Moq)对象更改Autofac模块中的注入

在当前的设计中,您不能注入模拟对象。最简单、变化最少的解决方案是在ServiceClass中添加一个Internal Cto'r:

internal ServiceClass(IContainer ioc)
{
    _ioc = ioc;     
}

然后使用属性InternalsVisibleTo来启用在测试类中使用C 'tor。

在arrange/setup/testInit阶段,用包含模拟对象的容器初始化被测类:

    [SetUp]
    public void TestInit()
    {
        Mock<ITimeConsume> moc = GetMockObj(...);
        builder.RegisterInstance(moc).As<ITimeConsume>();
        ...
        ...
        _target = new ServiceClass(builder.Build());
    }

我个人有多个容器实例。每个端点一个。

测试项目

public class AutofacLoader
{
    public static void Configure()
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule<ServiceProject.ServiceInjector>();
        builder.RegisterModule<LocalTestProject.AutofacModule>();
        Container = builder.Build();
    }
    public static IContainer Container { get; set; }
}

本地测试项目autofac模块可以自由地用特定的注册覆盖服务项目模块。

如果多个组件公开相同的服务,Autofac将使用最后注册的组件作为该服务的默认提供者:http://autofac.readthedocs.org/en/latest/register/registration.html#default-registrations

测试类

public void Test()
{
    AutofacLoader.Configure();
    var x = AutofacLoader.Container.Resolve<ITimeConsume>();
}