更优雅的在 Xunit 中使用依赖注入

Xunit.DependencyInjection 7.0 发布了

Intro

上次咱们已经介绍过一次大师的 Xunit.DependencyInjection https://www.cnblogs.com/weihanli/p/xuint-dependency-injection.html ,最近大师完成了 7.0 的重构而且已经正式发布,已经能够直接安装使用了html

7.0 为咱们带来了更好的编程体验,在 6.x 的版本中,咱们的 Startup 须要继承于 DependencyInjectionTestFramework 并且须要设置一个 assembly attribute,这在 7.0 中都不须要了,下面咱们来看看有了哪些变化git

Startup 的变化

首先来看大师给出的 diffgithub

-[assembly: TestFramework("Your.Test.Project.Startup", "Your.Test.Project")]

namespace Your.Test.Project
{
-   public class Startup : DependencyInjectionTestFramework
+   public class Startup
    {
-       public Startup(IMessageSink messageSink) : base(messageSink) { }

-       protected void ConfigureServices(IServiceCollection services)
+       public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IDependency, DependencyClass>();
        }

-       protected override IHostBuilder CreateHostBuilder() =>
-           base.CreateHostBuilder(assemblyName)
-               .ConfigureServices(ConfigureServices);

-       protected override void Configure(IServiceProvider provider)
+       public void Configure(IServiceProvider provider)
    }
}
  1. 移除了 TestFramework assembly attribute
  2. 再也不须要继承于 DependencyInjectionTestFramework
  3. 也由于上面的不须要继承,因此本来要 override 的方法能够不 override 了,原来是 protected 的方法如今须要改为 public

这样改了以后首先咱们在使用的时候无需知道 DependencyInjectionTestFramework 的存在了,并且能够更符合 asp.net core Startup 的使用习惯,能够屏蔽掉不少实现细节,用户只须要在 Startup 注册本身的逻辑便可,更为专一于本身的逻辑而无需关心框架所作的事情编程

新的 Startup 解析

我把上一篇文章写的示例用升级到了新的版本,下面是更新后的示例代码json

namespace XUnitDependencyInjectionSample
{
    public class Startup
    {
        // 自定义 HostBuilder ,能够没有这个方法,没有这个方法会使用默认的 hostBuilder,一般直接使用 `ConfigureHost` 应该就够用了
        // public IHostBuilder CreateHostBuilder()
        // {
        //     return new HostBuilder()
        //         .ConfigureAppConfiguration(builder =>
        //         {
        //             // 注册配置
        //             builder
        //                 .AddInMemoryCollection(new Dictionary<string, string>()
        //                 {
        //                     {"UserName", "Alice"}
        //                 })
        //                 .AddJsonFile("appsettings.json")
        //                 ;
        //         })
        //         .ConfigureServices((context, services) =>
        //         {
        //             // 注册自定义服务
        //             services.AddSingleton<IIdGenerator, GuidIdGenerator>();
        //             if (context.Configuration.GetAppSetting<bool>("XxxEnabled"))
        //             {
        //                 services.AddSingleton<IUserIdProvider, EnvironmentUserIdProvider>();
        //             }
        //         })
        //         ;
        // }

        // 自定义 host 构建
        public void ConfigureHost(IHostBuilder hostBuilder)
        {
            hostBuilder
                .ConfigureAppConfiguration(builder =>
                {
                    // 注册配置
                    builder
                        .AddInMemoryCollection(new Dictionary<string, string>()
                        {
                            {"UserName", "Alice"}
                        })
                        .AddJsonFile("appsettings.json")
                        ;
                })
                .ConfigureServices((context, services) =>
                {
                    // 注册自定义服务
                    services.AddSingleton<IIdGenerator, GuidIdGenerator>();
                    if (context.Configuration.GetAppSetting<bool>("XxxEnabled"))
                    {
                        services.AddSingleton<IUserIdProvider, EnvironmentUserIdProvider>();
                    }
                })
                ;
        }

        // 支持的形式:
        // ConfigureServices(IServiceCollection services)
        // ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext)
        // ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services)
        public void ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext)
        {
            services.TryAddSingleton<CustomService>();
        }

        // 能够添加要用到的方法参数,会自动从注册的服务中获取服务实例,相似于 asp.net core 里 Configure 方法
        public void Configure(IServiceProvider applicationServices, IIdGenerator idGenerator)
        {
            // 有一些测试数据要初始化能够放在这里
            // InitData();
        }
    }
}

在新的版本中 Startup 和 asp.net core 里的 Startup 更加相像了,app

会多一个 CreateHostBuilder/ConfigureHost(IHostBuilder) 的方法,容许用户自定义 Host 的构建,也能够没有这个方法框架

ConfigureServices 方法容许用户增长 HostBuilderContext 做为参数,能够经过 hostBuilderContext 来获取配置信息,也能够在 CreateHostBuilder/ConfigureHost(IHostBuilder) 里注册也是同样的asp.net

注册配置/服务和 asp.net core 里如出一辙,有数据或配置须要在项目启动时初始化的,能够放在 Configure 方法作,有点相似于 asp.net core 里 Startup 中的 Configure 方法,能够将须要的服务做为方法参数,执行时会自动从注册的服务中获取ide

Startup 的寻找方法

默认的 Startup 一般是 ProjectName.Startup,一般在项目根目录下建立一个 Startup 是不须要配置的,若是不是或不起做用,能够参考下面 Startup 的寻找规则测试

若是要使用一个特别的 Startup, 你能够经过在项目文件的 PropertyGroup 部分定义 XunitStartupAssemblyXunitStartupFullName,具体规则以下

<Project>
  <PropertyGroup>
    <XunitStartupAssembly>Abc</XunitStartupAssembly>
    <XunitStartupFullName>Xyz</XunitStartupFullName>
  </PropertyGroup>
</Project>
XunitStartupAssembly XunitStartupFullName Startup
Your.Test.Project.Startup, Your.Test.Project
Abc Abc.Startup, Abc
Xyz Xyz, Your.Test.Project
Abc Xyz Xyz, Abc

More

除了上面的 Startup 的改动以外,新版本还支持了 xunit 中 fixture 的依赖注入,彷佛是由一个外国小哥提的 PR, 详见:https://github.com/pengweiqhca/Xunit.DependencyInjection/pull/21

有了这个神器,在测试代码中使用依赖注入要方便不少了,尚未用起来的能够准备上手了~~

Reference

相关文章
相关标签/搜索