更優雅的在 Xunit 中使用依賴注入 Xunit.DependencyInjection 7.0 發佈了

Xunit.DependencyInjection 7.0 發佈了

Intro

上次我們已經介紹過一次大師的 Xunit.DependencyInjection https://www.cnblogs.com/weihanli/p/xuint-dependency-injection.html ,最近大師完成了 7.0 的重構並且已經正式發佈,已經可以直接安裝使用了

7.0 爲我們帶來了更好的編程體驗,在 6.x 的版本中,我們的 Startup 需要繼承於 DependencyInjectionTestFramework 而且需要設置一個 assembly attribute,這在 7.0 中都不需要了,下面我們來看看有了哪些變化

Startup 的變化

首先來看大師給出的 diff


-[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 解析

我把上一篇文章寫的示例用升級到了新的版本,下面是更新後的示例代碼

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 更加相像了,

會多一個 CreateHostBuilder/ConfigureHost(IHostBuilder) 的方法,允許用戶自定義 Host 的構建,也可以沒有這個方法

ConfigureServices 方法允許用戶增加 HostBuilderContext 作爲參數,可以通過 hostBuilderContext 來獲取配置信息,也可以在 CreateHostBuilder/ConfigureHost(IHostBuilder) 裏註冊也是一樣的

註冊配置/服務和 asp.net core 裏一模一樣,有數據或配置需要在項目啓動時初始化的,可以放在 Configure 方法做,有點類似於 asp.net core 裏 Startup 中的 Configure 方法,可以將需要的服務作爲方法參數,執行時會自動從註冊的服務中獲取

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章