xunitPlus 1.2.12

dotnet add package xunitPlus --version 1.2.12
                    
NuGet\Install-Package xunitPlus -Version 1.2.12
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="xunitPlus" Version="1.2.12" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="xunitPlus" Version="1.2.12" />
                    
Directory.Packages.props
<PackageReference Include="xunitPlus" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add xunitPlus --version 1.2.12
                    
#r "nuget: xunitPlus, 1.2.12"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package xunitPlus@1.2.12
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=xunitPlus&version=1.2.12
                    
Install as a Cake Addin
#tool nuget:?package=xunitPlus&version=1.2.12
                    
Install as a Cake Tool

xunit+ 框架使用指南

Inkslab

GitHub language codeSize

目录

框架介绍

xunit+ 是基于 xunit 测试框架,结合 inkslab 依赖注入框架开发的增强型单元测试框架。它支持:

  • 自动依赖注入:构造函数参数自动注入,无需手动创建测试对象
  • 灵活的启动配置:支持多种启动类配置方式
  • ASP.NET Core 集成:完美支持 IHostBuilderIServiceCollection
  • 测试步骤控制:通过 [Step] 特性控制测试执行顺序
  • HTTP 上下文支持:内置 IHttpContextAccessor 支持

快速开始

安装

通过 NuGet 包管理器安装:

PM> Install-Package xunitPlus

或通过 .NET CLI:

dotnet add package xunitPlus

基础用例

创建一个最简单的单元测试:

// 定义测试接口和实现
public interface ICalculator
{
    int Add(int a, int b);
}

public class Calculator : ICalculator
{
    public int Add(int a, int b) => a + b;
}

// 编写单元测试(构造函数参数自动注入)
public class CalculatorTests
{
    private readonly ICalculator _calculator;

    // 构造函数参数会自动从 DI 容器中注入
    public CalculatorTests(ICalculator calculator)
    {
        _calculator = calculator;
    }

    [Fact]
    public void Add_TwoNumbers_ReturnsSum()
    {
        // Arrange & Act
        var result = _calculator.Add(2, 3);

        // Assert
        Assert.Equal(5, result);
    }
}

注意:框架会自动扫描程序集并注册实现类,无需手动配置服务!

核心特性

1. 自动依赖注入

xunit+ 会自动扫描并注册程序集中的服务,支持:

  • 自动注册实现类到 DI 容器
  • 支持构造函数参数自动注入
  • 支持 ITestOutputHelper 等 xunit 特殊类型
配置程序集扫描规则

通过 [PatternSeek] 特性配置要扫描的程序集:

// 在 AssemblyInfo.cs 或测试类上标记
[assembly: XunitPlus.PatternSeek("MyProject.*")]
[assembly: XunitPlus.PatternSeek("ThirdParty.*.dll")]

或在 .csproj 文件中配置:

<ItemGroup>
    <AssemblyAttribute Include="XunitPlus.PatternSeek">
        <_Parameter1>MyProject.*</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

2. 启动类配置

xunit+ 提供多种启动类配置方式,优先级从高到低:

方式一:通过 [Startup] 特性指定
public class CustomStartup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<ICalculator, Calculator>();
    }
}

[Startup(typeof(CustomStartup))]
public class CalculatorTests
{
    private readonly ICalculator _calculator;

    public CalculatorTests(ICalculator calculator)
    {
        _calculator = calculator;
    }

    [Fact]
    public void Test_WithCustomStartup()
    {
        Assert.NotNull(_calculator);
    }
}
方式二:内嵌 Startup
public class NestedStartupTests
{
    private readonly ICalculator _calculator;

    public NestedStartupTests(ICalculator calculator)
    {
        _calculator = calculator;
    }

    [Fact]
    public void Test_WithNestedStartup()
    {
        Assert.NotNull(_calculator);
    }

    // 内嵌启动类
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<ICalculator, Calculator>();
        }
    }
}
方式三:程序集级别 Startup

在测试项目的命名空间下创建 Startup 类:

namespace MyProject.Tests
{
    // 程序集级别的启动类
    public class Startup
    {
        // 创建宿主构建器(可选)
        public IHostBuilder CreateHostBuilder() 
            => Host.CreateDefaultBuilder();

        // 配置宿主构建器(可选)
        public void ConfigureHost(IHostBuilder builder)
        {
            builder.ConfigureAppConfiguration((context, config) =>
            {
                config.AddJsonFile("appsettings.json");
            });
        }

        // 配置服务(可选)
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<ICalculator, Calculator>();
        }

        // 构建宿主(可选)
        public IHost BuildHost(IHostBuilder builder) => builder.Build();

        // 应用配置(可选)
        public void Configure(IServiceProvider provider)
        {
            // 应用启动配置
        }
    }
}
方式四:框架默认级别 XunitPlus.Startup
  • 注:自动按照指定的PatternSeekAttribute的规则(未指定时,默认为所有程序集)扫描程序集,自动注入当前单元测试类所需依赖。
public class DefaultStartupTests
{
    private readonly ICalculator _calculator;

    public DefaultStartupTests(ICalculator calculator)
    {
        _calculator = calculator;
    }

    [Fact]
    public void Test_WithNestedStartup()
    {
        Assert.NotNull(_calculator);
    }
}

注意

  1. 启动类可以支持唯一的构造函数参数类型为 Type,表示当前运行的单元测试类
  2. 启动类的所有方法均为可选,返回值类型为 void 的不可更改
  3. 不区分是否是静态方法或静态类

3. 测试步骤控制

使用 [Step] 特性控制测试执行顺序,确保测试按照指定顺序运行:

[Serializable]
public class OrderedTests
{
    private static int _counter = 0;

    [Fact]
    [Step(1)]
    public void FirstTest()
    {
        _counter++;
        Assert.Equal(1, _counter);
    }

    [Fact]
    [Step(2)]
    public void SecondTest()
    {
        _counter++;
        Assert.Equal(2, _counter);
    }

    [Fact]
    [Step(3)]
    public void ThirdTest()
    {
        _counter++;
        Assert.Equal(3, _counter);
    }
}

重要:需要在测试类上添加 [Serializable] 特性,否则测试顺序可能不会生效。

4. ASP.NET Core 集成测试

xunit+ 完美支持 ASP.NET Core 集成测试:

public class WebApiTests
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IConfiguration _configuration;

    public WebApiTests(
        IHttpContextAccessor httpContextAccessor,
        IConfiguration configuration)
    {
        _httpContextAccessor = httpContextAccessor;
        _configuration = configuration;
    }

    [Fact]
    public void Test_HttpContext_IsAvailable()
    {
        Assert.NotNull(_httpContextAccessor);
    }

    public class Startup
    {
        public IHostBuilder CreateHostBuilder()
            => Host.CreateDefaultBuilder();

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddHttpContextAccessor();
        }
    }
}

5. 便捷标记特性

框架提供多种便捷标记,可以混合使用(权重从上往下依次递增):

  • [Header] - 添加 HTTP 请求头
  • [Authorization] - 添加认证请求头
  • [HttpContext] - 创建 HttpContext 请求上下文(需继承)
  • [User] - 创建请求用户信息(需继承)
[Header("X-Custom-Header", "CustomValue")]
[Authorization("Bearer", "your-token-here")]
public class ApiTests
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public ApiTests(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    [Fact]
    public void Test_Headers_AreSet()
    {
        var httpContext = _httpContextAccessor.HttpContext;
        Assert.NotNull(httpContext);
    }
}

使用指南

项目配置

1. 创建测试项目
dotnet new xunit -n MyProject.Tests
cd MyProject.Tests
dotnet add package xunitPlus
2. 配置 .csproj 文件
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <IsPackable>false</IsPackable>
        <IsTestProject>true</IsTestProject>
    </PropertyGroup>

    <ItemGroup>
        
        <AssemblyAttribute Include="XunitPlus.PatternSeek">
            <_Parameter1>MyProject.*</_Parameter1>
        </AssemblyAttribute>
    </ItemGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
        <PackageReference Include="xunit" Version="2.7.0" />
        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.8" />
        <PackageReference Include="xunitPlus" Version="1.2.8" />
    </ItemGroup>
</Project>

高级用法

1. 自定义服务生命周期

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Transient:每次请求创建新实例
        services.AddTransient<ITransientService, TransientService>();
        
        // Scoped:每个作用域创建一个实例
        services.AddScoped<IScopedService, ScopedService>();
        
        // Singleton:整个应用生命周期只创建一个实例
        services.AddSingleton<ISingletonService, SingletonService>();
    }
}

2. 使用配置文件

public class Startup
{
    public IHostBuilder CreateHostBuilder()
        => Host.CreateDefaultBuilder();

    public void ConfigureHost(IHostBuilder builder)
    {
        builder.ConfigureAppConfiguration((context, config) =>
        {
            config.SetBasePath(Directory.GetCurrentDirectory())
                  .AddJsonFile("appsettings.json", optional: false)
                  .AddEnvironmentVariables();
        });
    }

    public void ConfigureServices(IServiceCollection services, HostBuilderContext context)
    {
        var configuration = context.Configuration;
        services.Configure<MyOptions>(configuration.GetSection("MyOptions"));
    }
}

3. 使用 ITestOutputHelper

public class OutputTests
{
    private readonly ITestOutputHelper _output;
    private readonly ICalculator _calculator;

    public OutputTests(ITestOutputHelper output, ICalculator calculator)
    {
        _output = output;
        _calculator = calculator;
    }

    [Fact]
    public void Test_WithOutput()
    {
        _output.WriteLine("开始测试...");
        var result = _calculator.Add(2, 3);
        _output.WriteLine($"计算结果: {result}");
        Assert.Equal(5, result);
    }
}

常见问题

Q1: 为什么我的服务没有被注入?

检查以下几点

  1. 确认已配置 [PatternSeek] 特性,且匹配程序集名称
  2. 确认服务类是 public
  3. 确认服务类有公共构造函数
  4. 查看测试输出中的诊断信息

Q2: 如何禁用自动程序集扫描?

如果不想使用自动扫描,可以完全手动配置服务:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // 手动注册所有服务
        services.AddTransient<IService1, Service1>();
        services.AddTransient<IService2, Service2>();
    }
}

Q3: 多个测试类如何共享启动配置?

使用程序集级别的 Startup 类(见方式三)。

Q4: [Step] 特性不生效怎么办?

确保测试类添加了 [Serializable] 特性:

[Serializable]
public class OrderedTests
{
    [Fact]
    [Step(1)]
    public void FirstTest() { }
    
    [Fact]
    [Step(2)]
    public void SecondTest() { }
}

Q5: 如何在测试中使用 Moq 等 Mock 框架?

完全兼容,可以混合使用:

public class MockTests
{
    [Fact]
    public void Test_WithMock()
    {
        // 使用 Moq 创建 Mock 对象
        var mockCalculator = new Mock<ICalculator>();
        mockCalculator.Setup(x => x.Add(It.IsAny<int>(), It.IsAny<int>()))
                     .Returns(10);

        var result = mockCalculator.Object.Add(2, 3);
        Assert.Equal(10, result);
    }
}

示例项目

查看完整示例:

  • tests/XunitPlus.Tests - 基础功能示例
  • tests/XunitPlus.Auto.Tests - 自动注入和步骤控制示例
  • tests/XunitPlus.User.Tests - HTTP 上下文和用户场景示例

项目结构

xunitplus/
 src/
    XunitPlus/                           # 核心框架代码
        XunitPlusXunitTestFramework.cs   # 测试框架入口
        XunitPlusTestFrameworkExecutor.cs # 测试执行器
        StepOrderer.cs                   # 步骤排序器
        Startup.cs                       # 默认启动类
        StartupAttribute.cs              # 启动类特性
        StepAttribute.cs                 # 测试步骤特性
        ...
 tests/
     XunitPlus.Tests/              # 基础测试
     XunitPlus.Auto.Tests/         # 自动注入测试
     XunitPlus.User.Tests/         # 用户场景测试

相关链接

许可证

本项目基于 MIT 许可证开源。详见 LICENSE 文件。

贡献

欢迎提交 Issue 和 Pull Request!


Happy Testing!

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.12 33 2/6/2026
1.2.11 90 1/29/2026
1.2.10 82 1/29/2026
1.2.9 201 10/24/2025
1.2.8 278 6/9/2025
1.2.7 235 11/26/2024
1.2.6 296 9/11/2024
1.2.5 169 8/27/2024
1.2.4 285 6/30/2024
1.2.3 205 6/20/2024
1.2.2 208 6/20/2024
1.2.1 284 5/17/2024
1.2.0 230 3/27/2024
1.1.0 191 3/27/2024
1.0.0 235 3/26/2024