This package contains the necessary classes and features to leverage Xunit's fixture class for Microsoft dependency injection framework.
$ dotnet add package Xunit.Microsoft.DependencyInjectionXunit does not support any built-in dependency injection features, therefore developers have to come up with a solution to recruit their favourite dependency injection framework in their tests.
This library brings Microsoft's dependency injection container to Xunit by leveraging Xunit's fixture pattern and provides three approaches for dependency injection in your tests:
[Inject] attributes on properties_fixture.GetService<T>(_testOutputHelper) (fully backward compatible)appsettings.json, user secrets, and environment variablesIAsyncDisposable services are disposed asynchronously during fixture teardownAlso please check the migration guide from xUnit for test authors.
<PackageReference Include="xunit.v3" Version="3.2.0" />
Before you begin, ensure you have:
First add the following nuget package to your Xunit test project:
Install-Package Xunit.Microsoft.DependencyInjection
dotnet add package Xunit.Microsoft.DependencyInjection
<PackageReference Include="Xunit.Microsoft.DependencyInjection" Version="9.2.0" />
✨ That's it! All required Microsoft.Extensions dependencies are now automatically included with the package, so you don't need to manually add them to your test project.
Here's a minimal example to get you started quickly:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Microsoft.DependencyInjection.Abstracts;
public class MyTestFixture : TestBedFixture
{
protected override void AddServices(IServiceCollection services, IConfiguration? configuration)
=> services
.AddTransient<IMyService, MyService>()
.AddScoped<IMyScopedService, MyScopedService>();
protected override ValueTask DisposeAsyncCore() => new();
protected override IEnumerable<TestAppSettings> GetTestAppSettings()
{
yield return new() { Filename = "appsettings.json", IsOptional = true };
}
}
using Xunit.Microsoft.DependencyInjection.Abstracts;
using Xunit.Microsoft.DependencyInjection.Attributes;
[Collection("Dependency Injection")]
public class MyTests : TestBedWithDI<MyTestFixture>
{
[Inject] private IMyService MyService { get; set; } = null!;
[Inject] private IMyScopedService MyScopedService { get; set; } = null!;
public MyTests(ITestOutputHelper testOutputHelper, MyTestFixture fixture)
: base(testOutputHelper, fixture) { }
[Fact]
public async Task TestMyService()
{
// Your services are automatically injected and ready to use
var result = await MyService.DoSomethingAsync();
Assert.NotNull(result);
}
}
[CollectionDefinition("Dependency Injection")]
public class MyTraditionalTests : TestBed<MyTestFixture>
{
public MyTraditionalTests(ITestOutputHelper testOutputHelper, MyTestFixture fixture)
: base(testOutputHelper, fixture) { }
[Fact]
public async Task TestMyService()
{
// Get services from the fixture
var myService = _fixture.GetService<IMyService>(_testOutputHelper)!;
var result = await myService.DoSomethingAsync();
Assert.NotNull(result);
}
}
The abstract class of Xunit.Microsoft.DependencyInjection.Abstracts.TestBedFixture contains the necessary functionalities to add services and configurations to Microsoft's dependency injection container. Your concrete test fixture class must derive from this abstract class and implement the following abstract methods:
protected abstract void AddServices(IServiceCollection services, IConfiguration? configuration);
protected abstract IEnumerable<TestAppSettings> GetTestAppSettings();
protected abstract ValueTask DisposeAsyncCore();
Use DisposeAsyncCore() to clean up fixture-owned resources (for example, files, sockets, or external clients created by the fixture). Service cleanup for dependencies resolved from the DI container is handled by the framework during async teardown.
TestBedFixture now ignores any TestAppSettings entries whose Filename is null or empty before calling AddJsonFile. That means you can safely return placeholder descriptors or rely only on environment variables; optional JSON files can simply leave Filename blank and the framework skips them automatically when building the configuration root.
GetConfigurationFiles(...) method returns a collection of the configuration files in your Xunit test project to the framework. AddServices(...) method must be used to wire up the implemented services.
Secret manager is a great tool to store credentials, API keys, and other secret information for development purposes. This library has started supporting user secrets from version 8.2.0 onwards. To utilize user secrets in your tests, simply override the virtual method below from the TestBedFixture class:
protected override void AddUserSecrets(IConfigurationBuilder configurationBuilder);
There are two method that you can use to access the wired up service depending on your context:
public T GetScopedService<T>(ITestOutputHelper testOutputHelper);
public T GetService<T>(ITestOutputHelper testOutputHelper);
To access async scopes simply call the following method in the abstract fixture class:
public AsyncServiceScope GetAsyncScope(ITestOutputHelper testOutputHelper);
You can call the following method to access the keyed already-wired up services:
T? GetKeyedService<T>([DisallowNull] string key, ITestOutputHelper testOutputHelper);
New in this version (ver 9.2.0 and beyond): The library now supports constructor-style dependency injection while maintaining full backward compatibility with the existing fixture-based approach.
For cleaner test code, inherit from TestBedWithDI<TFixture> instead of TestBed<TFixture> and use the [Inject] attribute:
public class PropertyInjectionTests : TestBedWithDI<TestProjectFixture>
{
[Inject]
public ICalculator? Calculator { get; set; }
[Inject]
public IOptions<Options>? Options { get; set; }
public PropertyInjectionTests(ITestOutputHelper testOutputHelper, TestProjectFixture fixture)
: base(testOutputHelper, fixture)
{
// Dependencies are automatically injected after construction
}
[Fact]
public async Task TestWithCleanSyntax()
{
// Dependencies are immediately available - no fixture calls needed
Assert.NotNull(Calculator);
var result = await Calculator.AddAsync(5, 3);
Assert.True(result > 0);
}
}
Use the [Inject("key")] attribute for keyed services:
public class PropertyInjectionTests : TestBedWithDI<TestProjectFixture>
{
[Inject("Porsche")]
internal ICarMaker? PorscheCarMaker { get; set; }
[Inject("Toyota")]
internal ICarMaker? ToyotaCarMaker { get; set; }
[Fact]
public void TestKeyedServices()
{
Assert.NotNull(PorscheCarMaker);
Assert.NotNull(ToyotaCarMaker);
Assert.Equal("Porsche", PorscheCarMaker.Manufacturer);
Assert.Equal("Toyota", ToyotaCarMaker.Manufacturer);
}
}
The TestBedWithDI class provides convenience methods that don't require the _testOutputHelper parameter:
protected T? GetService<T>()
protected T? GetScopedService<T>()
protected T? GetKeyedService<T>(string key)
[Inject] attribute on propertiesTestBed<TFixture> code continues to work unchangedYou can migrate existing tests gradually:
TestBed<TFixture> with fixture methodsTestBedWithDI<TFixture> and use both [Inject] properties and fixture methodsFor true constructor injection into service classes, see CONSTRUCTOR_INJECTION.md for the factory-based approach.
Test developers can add their own desired logger provider by overriding AddLoggingProvider(...) virtual method defined in TestBedFixture class.
Your Xunit test class must be derived from Xunit.Microsoft.DependencyInjection.Abstracts.TestBed<T> class where T should be your fixture class derived from TestBedFixture.
Also, the test class should be decorated by the following attribute:
[CollectionDefinition("Dependency Injection")]
To have managed resources cleaned up, simply override the virtual method of Clear(). This is an optional step.
TestBedFixture performs async teardown and disposes the DI ServiceProvider asynchronously. This ensures container-managed services implementing IAsyncDisposable are disposed correctly during fixture teardown.
If you need additional async cleanup for fixture-owned resources, override DisposeAsyncCore():
public sealed class MyTestFixture : TestBedFixture
{
protected override ValueTask DisposeAsyncCore()
{
// Cleanup resources created/owned by the fixture itself.
return ValueTask.CompletedTask;
}
}
For a full working example, see AsyncDisposableTests and AsyncDisposableFixture in the examples project.
The library also has a bonus feature that simplifies running tests in order. The test class does not have to be derived from TestBed<T> class though and it can apply to all Xunit classes.
Decorate your Xunit test class with the following attribute and associate TestOrder(...) with Fact and Theory:
[TestCaseOrderer("Xunit.Microsoft.DependencyInjection.TestsOrder.TestPriorityOrderer", "Xunit.Microsoft.DependencyInjection")]
UserSecretsThis library's TestBedFixture abstract class exposes an instance of IConfigurationBuilder that can be used to support UserSecrets when configuring the test projects:
public IConfigurationBuilder ConfigurationBuilder { get; private set; }
📖 Complete Examples Documentation - Comprehensive guide with working code examples
TestBed<TFixture> and _fixture.GetService<T>(_testOutputHelper)PropertyInjectionTests.cs for examples using TestBedWithDI<TFixture> with [Inject] attributesFactoryConstructorInjectionTests.cs for experimental constructor injection scenariosKeyedServicesTests.cs for .NET 9.0 keyed service examplesUserSecretTests.cs for configuration and user secrets integrationAsyncDisposableTests.cs and Fixtures/AsyncDisposableFixture.cs for async teardown of IAsyncDisposable servicesAdvancedDependencyInjectionTests.cs for IOptions<T>, Func<T>, and Action<T> examples🏢 Digital Silo's unit tests and integration tests are using this library in production.
If you encounter build errors, ensure all required Microsoft.Extensions packages are installed with compatible versions.
appsettings.json is set to "Copy to Output Directory: Copy if newer" in file propertiesdotnet user-secrets initdotnet user-secrets set "SecretKey" "SecretValue"