More elegant use of dependency injection in xUnit

Time:2020-9-21

Xunit.DependencyInjection 7.0 was released

Intro

Last time we introduced the masterXunit.DependencyInjectionhttps://www.cnblogs.com/weihanli/p/xuint-dependency-injection.html Recently, the master has completed the refactoring of 7.0 and has been officially released, which can be directly installed and used

7.0 brings us a better programming experience. In version 6. X, ourStartupNeed to inherit fromDependencyInjectionTestFrameworkIn addition, an assembly attribute needs to be set, which is not needed in 7.0. Let’s see what changes have been made

Changes in startup

Let’s first look at the diff given by the master

-[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();
        }

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

-       protected override void Configure(IServiceProvider provider)
+       public void Configure(IServiceProvider provider)
    }
}
  1. RemovedTestFramework assembly attribute
  2. No longer need to inherit fromDependencyInjectionTestFramework
  3. Because the above does not need inheritance, so it was originallyoverrideCan we notoverrideYes, it wasprotectedThe method needs to be changed topublic

After this change, first of all, we don’t need to know when using itDependencyInjectionTestFrameworkAnd it can be more consistent asp.net coreStartupThe user can shield many implementation details by using theStartupYou can register your own logic and focus more on your own logic without paying attention to what the framework does

New startup analysis

I upgrade the example written in the last article to a new version. Here is the updated sample code

namespace XUnitDependencyInjectionSample
{
    public class Startup
    {
        //You can customize hostbuilder without this method. Without this method, the default hostbuilder will be used. Generally, it is enough to use 'configurehost' directly
        // public IHostBuilder CreateHostBuilder()
        // {
        //     return new HostBuilder()
        //         .ConfigureAppConfiguration(builder =>
        //         {
        //// registration configuration
        //             builder
        //                 .AddInMemoryCollection(new Dictionary()
        //                 {
        //                     {"UserName", "Alice"}
        //                 })
        //                 .AddJsonFile("appsettings.json")
        //                 ;
        //         })
        //         .ConfigureServices((context, services) =>
        //         {
        //// register custom service
        //             services.AddSingleton();
        //             if (context.Configuration.GetAppSetting("XxxEnabled"))
        //             {
        //                 services.AddSingleton();
        //             }
        //         })
        //         ;
        // }

        //Custom host build
        public void ConfigureHost(IHostBuilder hostBuilder)
        {
            hostBuilder
                .ConfigureAppConfiguration(builder =>
                {
                    //Registration configuration
                    builder
                        .AddInMemoryCollection(new Dictionary()
                        {
                            {"UserName", "Alice"}
                        })
                        .AddJsonFile("appsettings.json")
                        ;
                })
                .ConfigureServices((context, services) =>
                {
                    //Register custom service
                    services.AddSingleton();
                    if (context.Configuration.GetAppSetting("XxxEnabled"))
                    {
                        services.AddSingleton();
                    }
                })
                ;
        }

        //Forms of support:
        // ConfigureServices(IServiceCollection services)
        // ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext)
        // ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services)
        public void ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext)
        {
            services.TryAddSingleton();
        }

        //You can add the method parameters to be used, and the service instance will be automatically obtained from the registered service, similar to asp.net  Configure method in core
        public void Configure(IServiceProvider applicationServices, IIdGenerator idGenerator)
        {
            //Some test data to be initialized can be put here
            // InitData();
        }
    }
}

In the new versionStartupAnd asp.net From the coreStartupIt’s more similar,

There will be one moreCreateHostBuilder/ConfigureHost(IHostBuilder)This method allows users to customize the construction of the host, or there can be no such method

ConfigureServicesMethod allows users to addHostBuilderContextAs a parameter, you can use thehostBuilderContextTo get configuration information, you can also use theCreateHostBuilder/ConfigureHost(IHostBuilder)It’s the same thing to register in

Registration configuration / services and asp.net Core is as like as two peas. Data or configuration needs to be initialized at the start of the project.ConfigureThe method is similar to asp.net In the coreStartupMediumConfigureMethod, the required service can be used as the method parameter, and will be automatically obtained from the registered service when executing

How to find startup

defaultStartupUsuallyProjectName.Startup, which is usually created in the root of the projectStartupIt does not need to be configured. If it does not or does not work, you can refer to the search rules of startup below

If you want to use a specialStartupYou can use thePropertyGroupPartial definitionXunitStartupAssemblyandXunitStartupFullNameThe specific rules are as follows

Abc
    Xyz
XunitStartupAssembly XunitStartupFullName Startup
Your.Test.Project.Startup, Your.Test.Project
Abc Abc.Startup, Abc
Xyz Xyz, Your.Test.Project
Abc Xyz Xyz, Abc

More

Except for the ones aboveStartupIn addition to the changes in xUnit, the new version also supports dependency injection of fixture in xUnit, which seems to be a PR proposed by a foreign brother. For details, please refer to: https://github.com/pengweiqhca/Xunit.DependencyInjection/pull/21

With this artifact, it is much more convenient to use dependency injection in test code. Those that have not been used can be ready to use~~

Reference

  • https://github.com/pengweiqhca/Xunit.DependencyInjection
  • https://github.com/WeihanLi/SamplesInPractice/tree/master/XUnitDependencyInjectionSample