应选XUnit。它对.NET Core/.NET 5+生命周期管理更原生,支持IClassFixture和IAsyncLifetime,默认禁用并行测试类,避免共享状态干扰,且异步资源清理更安全。

集成测试该用 XUnit 还是 NUnit?
选 XUnit。它对 .NET Core / .NET 5+ 的生命周期管理(比如 IClassFixture、IAsyncLifetime)支持更原生,且默认禁用并行测试类(避免共享状态干扰),更适合验证跨组件协作。而 NUnit 的 [SetUp]/[TearDown] 在异步资源清理上容易漏掉 await,导致数据库连接未释放或 HTTP 服务残留。
实操建议:
- 用
XUnit的IClassFixture管理单个测试类共用的 SQLite 内存数据库实例,避免每个测试都重建库 - 若需启动真实 HTTP 服务,用
WebApplicationFactory(.NET 6+),它自动注入测试用的TestServer和内存管道,不走网络栈 - 别在
Dispose()里手动调用GC.Collect()——XUnit的 fixture 生命周期已保证资源释放时机
WebApplicationFactory 启动失败常见原因
最常遇到的是 System.InvalidOperationException: No service for type 'Microsoft.Extensions.Hosting.IHostEnvironment' has been registered.,本质是测试宿主没正确复用生产环境的 Program.cs 构建逻辑。
关键点:
- 确保测试项目引用了和主项目完全一致的
Microsoft.AspNetCore.App共享框架版本,否则WebApplication.CreateBuilder()行为不一致 - 不要重写
ConfigureWebHost时直接 newWebHostBuilder()——必须调用builder.ConfigureWebHostDefaults()或显式注册IHostEnvironment - 如果主项目用了
AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false),测试中也得同步设置,否则TestServer发出的请求会绕过内存管道、直连本地端口
数据库集成测试:用 SQLite 内存模式还是 Docker 容器?
开发阶段优先选 SQLite 内存模式(Data Source=:memory:),启动快、无外部依赖;但必须接受它的 SQL 方言限制——比如不支持 JSON_EXTRACT、窗口函数、部分索引语法。一旦测试中出现 SQLite Error 1: 'no such function: JSON_EXTRACT',就得切到真实数据库。
切换策略:
- 用
IDbConnection抽象隔离数据访问层,测试时注入SqliteConnection,CI 环境注入SqlConnection - Docker 方式别用
latest镜像——固定为mcr.microsoft.com/mssql/server:2019-latest或对应版本,避免 CI 因镜像更新突然失败 - 每次测试前执行
DROP DATABASE IF EXISTS TestDb; CREATE DATABASE TestDb;,别依赖迁移脚本——EF Core 的MigrateAsync()在容器启动完成前可能抛SqlException连接超时
为什么 [Theory] + [MemberData] 在集成测试里容易变慢?
因为 MemberData 是静态方法,所有测试用例共享同一组 fixture 实例,如果数据构造逻辑里包含耗时操作(如读取大 JSON 文件、生成 1000 条模拟订单),它会在测试发现阶段就执行一次,拖慢整个测试集加载速度。
改进方式:
- 把数据生成移到 fixture 的
InitializeAsync()中,按需懒加载 - 用
[ClassData]替代[MemberData],让数据源本身实现IEnumerable,控制迭代时机 - 对高开销测试(如文件上传、第三方 API 调用),加
[Trait("Category", "External")]并在 CI 中单独跑,本地开发时用--filter "Category!=External"跳过
真正难处理的是事务边界——比如一个测试里调用了两个微服务,它们各自提交事务,你没法回滚。这种场景下,集成测试不是为了“验证正确性”,而是“暴露耦合”。得靠契约测试(Pact)或流量录制(WireMock)来解。










