Autofac 集成测试 在 ConfigureContainer 之后进行 Mock 注入

本文介绍了如何在 Autofac 集成测试中,实现在 ConfigureContainer 后进行 Mock 注入。通过创建 FakeAutofacServiceProviderFactory 并在测试项目中使用,能够在依赖注入收集完成后注入 Mock 类型,覆盖业务代码中的真实对象。文中详细阐述了实现方法、原理,包括方法调用顺序,以及如何利用 Autofac 模块化的特性实现测试类型注入。
摘要由CSDN通过智能技术生成

在使用 Autofac 框架进行开发后,编写集成测试时,需要用 Mock 的用于测试的模拟的类型去代替容器里面已注入的实际类型,也就需要在 Autofac 完全收集完成之后,再次注入模拟的对象进行覆盖原有业务代码注册的正式对象。但 Autofac 默认没有提供此机制,我阅读了 Autofac 的源代码之后,创建了一些辅助代码,实现了此功能。本文将告诉大家如何在集成测试里面,在使用了 Autofac 的项目里面,在所有收集完成之后,注入用于测试的 Mock 类型,和 Autofac 接入的原理

背景

为什么选择使用 Autofac 框架?原因是在此前的 WPF 项目里面,有使用过的是 MEF 和 Autofac 两个框架,而 MEF 的性能比较糟心。解决 MEF 性能问题的是 VS-MEF 框架。在后续开发的一个 ASP.NET Core 项目里面,也就自然选用了 Autofac 框架

对比原生的 ASP.NET Core 自带的 DI 框架,使用 Autofac 的优势在于支持模块化的初始化,支持属性注入

默认的 Autofac 可以通过 Autofac.Extensions.DependencyInjection 将 Autofac 和 dotnet 通用依赖注入框架合入在一起,但在 Autofac 里面的定制要求是在 Startup 的 ConfigureContainer 函数里面进行依赖注入,也就是在默认的 ASP.NET Core 里面没有提供更靠后的依赖注入方法,可以在完成收集之后,再次注入测试所需要的类型,覆盖业务代码里面的实际对象

需求

假定在一个应用,如 ASP.NET Core 应用里面,进行集成测试,想要在集成测试里面,使用项目里面的依赖注入关系,只是将部分类型替换为测试项目里面的模拟的类型

而在应用里面,实际的业务类型是在 Autofac 的 Module 进行注入的。在应用里面的大概逻辑如下,在 Program 的 CreateHostBuilder 函数里面通过 UseServiceProviderFactory 方法使用 Autofac 替换 原生 的框架

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                // 使用 auto fac 代替默认的 IOC 容器 
                .UseServiceProviderFactory(new AutofacServiceProviderFactory());

在 Startup 里面添加 ConfigureContainer 方法,代码如下

        public void ConfigureContainer(ContainerBuilder builder)
        {
            
        }

在 ConfigureContainer 里面注入具体的需要初始化的业务模块,例如 FooModule 模块。在具体模块里面注入实际业务的类型,如 Foo 类型,代码如下

    public class Startup
    {
    	// 忽略代码

        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterModule(new FooModule());
        }
    }

    class FooModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            Console.WriteLine($"06 FooModule");

            builder.RegisterType<Foo>().As<IFoo>();
        }
    }

    public class Foo : IFoo
    {
    }

    public interface IFoo
    {
    }

现在的需求是在集成测试里面,将 IFoo 的实际类型从 Foo 替换为 TestFoo 类型

在集成测试项目里面,可以使用如下代码获取实际的项目的依赖注入收集

                var hostBuilder = Host.CreateDefaultBuilder()
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                        webBuilder.UseTestServer(); //关键是多了这一行建立TestServer
                    })
                    // 使用 auto fac 代替默认的 IOC 容器 
                    .UseServiceProviderFactory(new AutofacServiceProviderFactory());

                var host = hostBuilder.Build();

                var foo = host.Services.GetService<IFoo>();

以上的 foo 就是从收集的容器里面获取的 IFoo 对象,以上代码获取到的是业务代码的 Foo 类型对象。假定需要让容器里面的 IFoo 的实际类型作为测试的 TestFoo 类型,就需要在实际项目的依赖注入收集完成之前,进行测试的注入

但实际上没有在 Autofac 里面找到任何的辅助方法可以用来实现此功能。如果是默认的应用框架,可以在 ConfigureWebHostDefaults 函数之后,通过 ConfigureServices 函数覆盖在 Startup 的 ConfigureServices 函数注入的类型

实现方法

实现的方法是很简单的,关于此实现为什么能解决问题还请参阅下文的原理部分

集成测试项目不需要改动原有的业务项目即可完成测试,实现方法是在集成测试项目里面添加 FakeAutofacServiceProviderFactory 用来替换 Autofac 的 AutofacServiceProviderFactory 类型,代码如下

    class FakeAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
    {
        public FakeAutofacServiceProviderFactory(
            ContainerBuildOptions containerBuildOptions = ContainerBuildOptions.None,
            Action<ContainerBuilder>? configurationActionOnBefore = null,
            Action<ContainerBuilder>? configurationActionOnAfter = null)
        {
            _configurationActionOnAfter = configurationActionOnAfter;
            AutofacServiceProviderFactory =
                new AutofacServiceProviderFactory(containerBuildOptions, configurationActionOnBefore);
        }

        private AutofacServiceProviderFactory AutofacServiceProviderFactory { get; }
        private readonly Action<ContainerBuilder>? _configurationActionOnAfter;

        public ContainerBuilder CreateBuilder(IServiceCollection services)
        {
            return AutofacServiceProviderFactory.CreateBuilder(services);
        }

        public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
        {
            _configurationActionOnAfter?.Invoke(containerBuilder);
            return AutofacServiceProviderFactory.CreateServiceProvider(containerBuilder);
        }
    }

可以看到本质的 FakeAutofacServiceProviderFactory 实现就是通过 AutofacServiceProviderFactory 的属性实现,只是在 Cre

要在 MVC5 中使用 Autofac 进行配置文件注入,需要按照以下步骤进行操作: 1. 安装 AutofacAutofac.Mvc5 NuGet 包。 2. 创建一个类来注册依赖项。例如,可以创建一个名为 AutofacConfig 的类。 3. 在 AutofacConfig 类中创建一个静态方法来注册依赖项。该方法应该接受一个 IContainerBuilder 对象作为参数。 4. 在 Register 方法中,使用 RegisterControllers 方法注册控制器。 5. 使用 RegisterAssemblyModules 方法注册程序集中的所有模块。 6. 使用 RegisterModelBinders 方法注册模型绑定器。 7. 使用 RegisterFilterProvider 方法注册筛选器提供程序。 8. 在 Global.asax.cs 文件中的 Application_Start 方法中,调用 AutofacConfig 类的 Register 方法,并将其传递给一个新的 ContainerBuilder 对象。 9. 在 Global.asax.cs 文件中的 Application_EndRequest 方法中,调用 AutofacDependencyResolver 类的 Dispose 方法以清理依赖项。 下面是一个示例 AutofacConfig 类,用于在 MVC5 中实现配置文件注入: ```csharp using Autofac; using Autofac.Integration.Mvc; using System.Web.Mvc; public class AutofacConfig { public static void Register(ContainerBuilder builder) { builder.RegisterControllers(typeof(MvcApplication).Assembly); builder.RegisterAssemblyModules(typeof(MvcApplication).Assembly); builder.RegisterModelBinders(typeof(MvcApplication).Assembly); builder.RegisterFilterProvider(); // 注册依赖项 builder.Register(c => new MyService(c.Resolve<MyDependency>())).As<IMyService>().InstancePerRequest(); builder.RegisterType<MyDependency>().As<IMyDependency>().SingleInstance(); } } ``` 在 Global.asax.cs 文件中,可以像这样调用 AutofacConfig 类的 Register 方法: ```csharp protected void Application_Start() { var builder = new ContainerBuilder(); AutofacConfig.Register(builder); var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); // ... } ``` 然后,在 Global.asax.cs 文件中的 Application_EndRequest 方法中,可以像这样调用 AutofacDependencyResolver 类的 Dispose 方法: ```csharp protected void Application_EndRequest() { var resolver = DependencyResolver.Current as AutofacDependencyResolver; if (resolver != null) { resolver.Dispose(); } } ``` 以上就是在 MVC5 中使用 Autofac 进行配置文件注入的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值