https://github.com/umplify/xunit-dependency-injection
:fire: A small library to help .NET developers leverage Microsoft's dependency injection framework in their Xunit-powered test projects
https://github.com/umplify/xunit-dependency-injection
c-sharp csharp csharp-lib csharp-library dependency-injection dependencyinjection dotnet dotnet-core dotnetcore microsoft-dependency-injection test testing xunit xunit-framework xunit-frameworks xunit-runner xunit-test xunit-tests
Last synced: 8 days ago
JSON representation
:fire: A small library to help .NET developers leverage Microsoft's dependency injection framework in their Xunit-powered test projects
- Host: GitHub
- URL: https://github.com/umplify/xunit-dependency-injection
- Owner: Umplify
- License: mit
- Created: 2020-11-15T15:02:59.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2025-12-21T15:41:50.000Z (2 months ago)
- Last Synced: 2026-01-29T00:27:02.910Z (30 days ago)
- Topics: c-sharp, csharp, csharp-lib, csharp-library, dependency-injection, dependencyinjection, dotnet, dotnet-core, dotnetcore, microsoft-dependency-injection, test, testing, xunit, xunit-framework, xunit-frameworks, xunit-runner, xunit-test, xunit-tests
- Language: C#
- Homepage: https://umplify.github.io/xunit-dependency-injection/
- Size: 464 KB
- Stars: 68
- Watchers: 2
- Forks: 15
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Citation: CITATION.cff
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
[](https://dev.azure.com/umplify/Grain/_build/latest?definitionId=18&branchName=refs%2Fpull%2F94%2Fmerge)


# Xunit Dependency Injection framework - .NET 10.0
Xunit 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:
1. **๐ Property Injection (Recommended)** - Clean, declarative syntax using `[Inject]` attributes on properties
2. **๐ง Traditional Fixture-Based** - Access services via `_fixture.GetService(_testOutputHelper)` (fully backward compatible)
3. **โก Factory Pattern** - True constructor injection into service classes (experimental)
## โจ Key Features
- ๐ฏ **Multiple injection patterns** - Choose the approach that fits your team's style
- ๐ **Keyed services support** - Full .NET 10.0 keyed services integration
- โ๏ธ **Configuration integration** - Support for `appsettings.json`, user secrets, and environment variables
- ๐งช **Service lifetime management** - Transient, Scoped, and Singleton services work as expected
- โป๏ธ **Async disposal support** - Container-managed `IAsyncDisposable` services are disposed asynchronously during fixture teardown
- ๐ฆ **Microsoft.Extensions ecosystem** - Built on the same DI container used by ASP.NET Core
- ๐ **Gradual migration** - Adopt new features incrementally without breaking existing tests
- ๐๏ธ **Production-ready** - Used by [Digital Silo](https://digitalsilo.io/) and other production applications
## Important: xUnit versions
* For **xUnit** packages use Xunit.Microsoft.DependencyInjection versions **up to** 9.0.5
* For **xUnit.v3** packages use Xunit.Microsoft.DependencyInjection versions **from** 9.1.0
* For **.NET 10.0** use Xunit.Microsoft.DependencyInjection version **10.0.0 or later**
Also please check the [migration guide](https://xunit.net/docs/getting-started/v3/migration) from xUnit for test authors.
### Example on how to reference xunit.v3
```xml
```
## Getting started
### Prerequisites
Before you begin, ensure you have:
- **.NET 10.0 SDK** installed on your development machine
- **Visual Studio 2022** or **Visual Studio Code** with C# extension
- Basic understanding of dependency injection concepts
- Familiarity with xUnit testing framework
### Nuget package
First add the following [nuget package](https://www.nuget.org/packages/Xunit.Microsoft.DependencyInjection/) to your Xunit test project:
#### Package Manager Console
```ps
Install-Package Xunit.Microsoft.DependencyInjection
```
#### .NET CLI
```bash
dotnet add package Xunit.Microsoft.DependencyInjection
```
#### PackageReference (in your .csproj file)
```xml
```
**โจ 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.
### Quick Start Example
Here's a minimal example to get you started quickly:
#### 1. Create a Test Fixture
```csharp
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()
.AddScoped();
protected override ValueTask DisposeAsyncCore() => new();
protected override IEnumerable GetTestAppSettings()
{
yield return new() { Filename = "appsettings.json", IsOptional = true };
}
}
```
#### 2. Create Your Test Class (Property Injection - Recommended)
```csharp
using Xunit.Microsoft.DependencyInjection.Abstracts;
using Xunit.Microsoft.DependencyInjection.Attributes;
[Collection("Dependency Injection")]
public class MyTests : TestBedWithDI
{
[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);
}
}
```
#### 3. Alternative: Traditional Fixture Approach
```csharp
[CollectionDefinition("Dependency Injection")]
public class MyTraditionalTests : TestBed
{
public MyTraditionalTests(ITestOutputHelper testOutputHelper, MyTestFixture fixture)
: base(testOutputHelper, fixture) { }
[Fact]
public async Task TestMyService()
{
// Get services from the fixture
var myService = _fixture.GetService(_testOutputHelper)!;
var result = await myService.DoSomethingAsync();
Assert.NotNull(result);
}
}
```
### Setup your fixture
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:
```csharp
protected abstract void AddServices(IServiceCollection services, IConfiguration? configuration);
protected abstract IEnumerable 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
[Secret manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-8.0&tabs=windows#how-the-secret-manager-tool-works) 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:
```csharp
protected override void AddUserSecrets(IConfigurationBuilder configurationBuilder);
```
### Access the wired up services
There are two method that you can use to access the wired up service depending on your context:
```csharp
public T GetScopedService(ITestOutputHelper testOutputHelper);
public T GetService(ITestOutputHelper testOutputHelper);
```
To access async scopes simply call the following method in the abstract fixture class:
```csharp
public AsyncServiceScope GetAsyncScope(ITestOutputHelper testOutputHelper);
```
### Accessing the keyed wired up services in .NET 9.0
You can call the following method to access the keyed already-wired up services:
```csharp
T? GetKeyedService([DisallowNull] string key, ITestOutputHelper testOutputHelper);
```
## Constructor Dependency Injection
**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.
### Property Injection with TestBedWithDI (Recommended)
For cleaner test code, inherit from `TestBedWithDI` instead of `TestBed` and use the `[Inject]` attribute:
```csharp
public class PropertyInjectionTests : TestBedWithDI
{
[Inject]
public ICalculator? Calculator { get; set; }
[Inject]
public IOptions? 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);
}
}
```
### Keyed Services with Property Injection
Use the `[Inject("key")]` attribute for keyed services:
```csharp
public class PropertyInjectionTests : TestBedWithDI
{
[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);
}
}
```
### Convenience Methods
The `TestBedWithDI` class provides convenience methods that don't require the `_testOutputHelper` parameter:
```csharp
protected T? GetService()
protected T? GetScopedService()
protected T? GetKeyedService(string key)
```
### Benefits of Constructor Dependency Injection
- โ
**Clean, declarative syntax** - Use `[Inject]` attribute on properties
- โ
**No manual fixture calls** - Dependencies available immediately in test methods
- โ
**Full keyed services support** - Both regular and keyed services work seamlessly
- โ
**Backward compatible** - All existing `TestBed` code continues to work unchanged
- โ
**Gradual migration** - Adopt new approach incrementally without breaking existing tests
### Migration Guide
You can migrate existing tests gradually:
1. **Keep existing approach** - Continue using `TestBed` with fixture methods
2. **Hybrid approach** - Change to `TestBedWithDI` and use both `[Inject]` properties and fixture methods
3. **Full migration** - Use property injection for all dependencies for cleanest code
### Factory Pattern (Experimental)
For true constructor injection into service classes, see [CONSTRUCTOR_INJECTION.md](CONSTRUCTOR_INJECTION.md) for the factory-based approach.
### Adding custom logging provider
Test developers can add their own desired logger provider by overriding ```AddLoggingProvider(...)``` virtual method defined in ```TestBedFixture``` class.
### Preparing Xunit test classes
Your Xunit test class must be derived from ```Xunit.Microsoft.DependencyInjection.Abstracts.TestBed``` class where ```T``` should be your fixture class derived from ```TestBedFixture```.
Also, the test class should be decorated by the following attribute:
```csharp
[CollectionDefinition("Dependency Injection")]
```
#### Clearing managed resources
To have managed resources cleaned up, simply override the virtual method of `Clear()`. This is an optional step.
#### Clearing managed resources asynchronously
`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()`:
```csharp
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.
## Running tests in order
The library also has a bonus feature that simplifies running tests in order. The test class does not have to be derived from ```TestBed``` 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```:
```csharp
[TestCaseOrderer("Xunit.Microsoft.DependencyInjection.TestsOrder.TestPriorityOrderer", "Xunit.Microsoft.DependencyInjection")]
```
## Supporting configuration from `UserSecrets`
This library's `TestBedFixture` abstract class exposes an instance of `IConfigurationBuilder` that can be used to support `UserSecrets` when configuring the test projects:
```csharp
public IConfigurationBuilder ConfigurationBuilder { get; private set; }
```
## Examples
๐ **[Complete Examples Documentation](Examples.md)** - Comprehensive guide with working code examples
* **[Live Examples](https://github.com/Umplify/xunit-dependency-injection/tree/main/examples/Xunit.Microsoft.DependencyInjection.ExampleTests)** - View the complete working examples that demonstrate all features
* **Traditional approach**: See examples using `TestBed` and `_fixture.GetService(_testOutputHelper)`
* **Property injection**: See `PropertyInjectionTests.cs` for examples using `TestBedWithDI` with `[Inject]` attributes
* **Factory pattern**: See `FactoryConstructorInjectionTests.cs` for experimental constructor injection scenarios
* **Keyed services**: See `KeyedServicesTests.cs` for .NET 9.0 keyed service examples
* **Configuration**: See `UserSecretTests.cs` for configuration and user secrets integration
* **Async disposal**: See `AsyncDisposableTests.cs` and `Fixtures/AsyncDisposableFixture.cs` for async teardown of `IAsyncDisposable` services
* **Advanced patterns**: See `AdvancedDependencyInjectionTests.cs` for `IOptions`, `Func`, and `Action` examples
๐ข [Digital Silo](https://digitalsilo.io/)'s unit tests and integration tests are using this library in production.
### Troubleshooting Common Issues
#### Missing Dependencies
If you encounter build errors, ensure all required Microsoft.Extensions packages are installed with compatible versions.
#### Configuration File Issues
- Ensure `appsettings.json` is set to "Copy to Output Directory: Copy if newer" in file properties
- Configuration files must be valid JSON format
#### User Secrets Issues
- Initialize user secrets: `dotnet user-secrets init`
- Set secrets: `dotnet user-secrets set "SecretKey" "SecretValue"`
#### xUnit Version Compatibility
- For **xUnit** packages use Xunit.Microsoft.DependencyInjection versions **up to** 9.0.5
- For **xUnit.v3** packages use Xunit.Microsoft.DependencyInjection versions **from** 9.1.0
### Need Help?
- ๐ **[Complete Examples Documentation](Examples.md)** - Step-by-step examples for all features
- ๐ **[GitHub Issues](https://github.com/Umplify/xunit-dependency-injection/issues)** - Report bugs or request features
- ๐ฆ **[NuGet Package](https://www.nuget.org/packages/Xunit.Microsoft.DependencyInjection/)** - Latest releases and changelog
- ๐ **[Migration Guide](https://xunit.net/docs/getting-started/v3/migration)** - For xUnit.v3 migration