Using dependency injection in xUnit test projects

Time:2021-10-12

Using dependency injection in xUnit test projects

Intro

I have written several articles on xUnit dependency injection before. Today’s article will combine my sharing on. Net conf to more systematically share the application cases in the test.

I want to share this topic because I think testing is a very important part in our development process. An important indicator of high-quality projects is test coverage. At the same time, dependency injection has become an indispensable part of modern applications. Our. Net core has integrated dependency injection from the beginning, Dependency injection cannot be absent for test projects.

XUnit is the most used test component in. Net,Xunit.DependencyInjectionIt is an extension of xUnit dependency injection written by the master. It is based on MicrosoftGenericHost(general host). Using it, we can easily implement dependency injection and integrate well with. Net core.

How it works

How does it work? Let’s take a look at its execution process. Its execution process is divided into four steps

First, you need to build a host, then start the host, execute the test case after startup, and finally terminate the host

执行流程

How is the host built? Let’s take a look. The construction of host is also divided into four steps

The first step is to create aHostBuilder, in most cases, we don’t need to use this method, just use the default implementation

The second step is to configure the host and make some custom configurations for the host

Step 3: configure the service and register the required services

Step four,Configure, you can do some initialization configuration, such as configuration initialization and test data initialization

Host构建流程

We can create one in the test projectStartupClass to controlHostConstruction process of

Samples

Next, let’s look at some actual test examples. The examples are divided into three parts: first, some basic usages, then integration with other components, and finally some extended usages

Get Started

First, let’s take a lookStartupUsage of thisStartupAnd asp.net coreStartupIt is very similar, both in use and implementation. If you are interested, you can take a look at the source code and compare it. Let’s take a look at the use mode and feel it through the following example

If you only need to register for the service, go directly toStartupAdd aConfigureServicesMethod, in which you can register the services you need, which is not much different from asp.net core

If you need to do some initialization, you can add oneConfigureMethod to implement its own initialization logic. If you need to obtain the injected service instance during initialization, it can be directly used as a method parameter, similar to that in asp.net coreConfigureMethod, but there is no need to configure the HTTP request pipeline

If you need to use configuration, you can use configuration inConfigureHostMethod passedConfigureHostConfigurationThe extension method registers its own configuration

If you need to use configuration when registering a service, you canConfigureServicesMethodHostBuildContextParameters,HostBuilderContextMediumConfigurationThe object is inConfigureHostConfiguration registered in

If necessaryConfigureMethod, add one directlyIConfigurationThe method parameters are OK

Let’s take another look at how to use injected services in test cases. Generally, we will inject directly through the constructor and add the services to be injected in the construction method. In addition, we can also inject through method parameters and combineInlineDataandMemeberDataUsing, take a look at this example

IoC/AOP Integration

Next, let’s look at the integration with other components,AutoFacIt is a very popular IOC component,AspectCoreIt is an AOP framework written by big lemon. Let’s take these two as examples to see how to integrate third-party dependency injection and AOP components. We mentioned earlier that it is based on MicrosoftGenericHostAsp.net core is also based on asp.net from 3.0GenericHostSo how to integrate in asp.net core is the same here. Let’s take a look at the example and only use the correspondingServiceProviderFactoryIt’s OK. Is it very simple

Test Server Integration

Then let’s see how to communicate withTestServerIntegration,TestServerIt is mainly used for integration testingTestServerThe advantage is that it interacts based on memory. Without real HTTP requests and TCP links, it will be very efficient, and will not listen to a port, so there will be no port permission problem.

TestServerThere are two main steps to use. The first is the registration of services, which can be usedIHostBuilderorIWebHostBuilderofUseTestServerExtension method registrationTestServer, you can useIHostofGetTestClientExtend methods to register andTestServerInteractiveHttpClient

After the service is registered, it can be injected in the test caseHttpClientTo request an API or page, you can refer to this example

Extensions

Hosted Service

Then let’s look at some extended usage,IHostedServiceIt can be used to implement some initialization operations or background services. We can useIHostedServiceTo implement the ready check of the application, and then start to execute the test case after applying ready, which is very useful in some scenarios

The applications we deploy in k8s generally have oneHealthCheck/ReadinessCheckK8s’s liveness / readiness probe can detect the status of the application. Only after the application is ready can it provide services to the outside

This example is an example of usingIHostedServiceTo implement a demo that waits for the application to be ready before executing the test case

Note: the waiting here cannot beStartupofConfigureMethod becauseConfigureIs executed when the host is calledStartAsyncMethod, and the webserver has not been started at this time, so it cannot be obtainedTestClientYes, and we passedHostedServiceYou can execute our wait ready logic after the web server starts

ITestOutputHelperAccessor

In the test, if you want to output a log, you can only useITestOutputHelperTo output, use directlyConsole.Write[Line]You can’t see any output,ITestOutputHelperIt can only be used in test cases, but not in test services,Xunit.DependencyInjectionProvides aITestOutputHelperAccessorServices, similar toIHttpContextAccessor, we can use it to get information from custom servicesITestOutputHelperTo output the log

Here is a simple example

Logging

Look at another oneOutputHelperAccessorPractical application of,Xunit.DependencyInjectionAn extension of logging is provided, so that we can output the logs during the test process to better help us debug

The integration method is also relatively simple. You can refer to this exampleXunit.DependencyInjection.LoggingAfter that, inLoggerFactoryRegister inXunitTestOutputLoggerProviderthat will do

You can see that our logs are directly output. The default log level isInformation, soDebugLevel logs are not output. If necessary, a delegate can be provided during registration to control whether to output logs

Project Template

For your convenience, we provide a project template. You can directly create a test project through a command, which will include a default templateStartupYou no longer need to write your own methods. You only need to delete them as needed

defaultTargetFrameworkUsingnetcoreapp3.1, you can-f/--franeworkSpecify the target framework you want to use. For example, if you want to generate a net 5.0 project, you only need to specify-f net5.0That’s it

The generated content is as follows:

More

Finally, some helpful links are listed. The first is the source code of the project, the second is the source code of all the above examples, and the nuget package used is followed.

The code implementation of this xUnit extension is very worth learning. Many of them are very similar to the implementation of asp.net core. If necessary, you can look at the source code and learn.

I hope my sharing will be helpful to you. You can contact me at any time or directly build an issue on GitHub if you encounter any problems during use.

Reference

Recommended Today

SQL statement of three-level linkage of provinces, cities and counties

The first is the table creation statement Copy codeThe code is as follows: CREATE TABLE `t_address_province` ( `id` INT AUTO_ Increment primary key comment ‘primary key’,`Code ` char (6) not null comment ‘province code’,`Name ` varchar (40) not null comment ‘province name’)Engine = InnoDB default charset = utf8 comment = ‘province information table’; CREATE TABLE […]