更優雅的在 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

相關文章
相關標籤/搜索