c# 使用 WebApplicationFactory 进行 ASP.NET Core 并发集成测试

来源:这里教程网 时间:2026-02-21 17:38:40 作者:

WebApplicationFactory 默认不支持并发测试

直接在多个线程或

Task
中复用同一个
WebApplicationFactory<tstartup></tstartup>
实例发起 HTTP 请求,大概率会遇到连接复用冲突、HTTP/2 流错乱、或
HttpRequestException: Error while copying content to a stream
。这是因为
WebApplicationFactory
内部的
TestServer
虽然线程安全,但其默认配置的
HttpClient
实例(通过
CreateClient()
返回)**共享连接池且未隔离请求上下文**,尤其在高并发短时密集调用下容易触发底层
HttpHandler
状态竞争。

必须为每个并发测试任务创建独立 HttpClient 实例

不要复用

CreateClient()
返回的单个
HttpClient
对象。每次并发请求前都应调用一次
CreateClient()
,让
WebApplicationFactory
为你生成带独立连接管理的新客户端。

CreateClient()
是轻量操作,不重建应用或服务器,只新建
HttpClient
和关联的
TestServer
连接句柄
若手动复用
HttpClient
,需确保它被正确配置为支持并发(例如设置
MaxConnectionsPerServer
),但不如直接用
CreateClient()
简洁可靠
在 xUnit 的
[Theory]
+
Parallelizable
Task.WhenAll
场景中,务必在每个
Task
内部调用
CreateClient()
var factory = new WebApplicationFactory<Program>();
await Task.WhenAll(Enumerable.Range(0, 10).Select(async _ =>
{
    // ✅ 正确:每个任务独立获取 client
    using var client = factory.CreateClient();
    var response = await client.GetAsync("/api/values");
    response.EnsureSuccessStatusCode();
}));

注意 TestServer 的同步上下文与异步陷阱

TestServer
内部使用同步上下文模拟 ASP.NET Core 的请求生命周期,但在高并发下若测试代码意外捕获并重用
SynchronizationContext
(比如在 UI 测试项目中引入了 WinForms/WPF 引用),可能引发死锁或响应延迟。xUnit 默认无同步上下文,但某些自定义测试基类或第三方断言库可能干扰。

确认测试项目未引用
System.Windows.Forms
PresentationCore
避免在测试方法中调用
Task.Wait()
Result
—— 必须用
await
若需调试,可在
WebApplicationFactory
派生类中重写
CreateWebHostBuilder()
并添加日志中间件,观察并发请求是否被串行化处理

并发测试失败时优先检查依赖服务状态

WebApplicationFactory
启动的是完整应用,包括所有注册的单例服务(如
IMemoryCache
、数据库上下文、事件总线等)。并发请求共享这些实例,若服务内部状态非线程安全(例如手动实现的静态计数器、未加锁的字典写入),会导致间歇性失败。

检查
Program.cs
中是否将有状态对象注册为
AddSingleton
却未做同步保护
对集成测试中的数据库操作,建议使用临时内存数据库(
AddDbContext<appdbcontext>(o => o.UseInMemoryDatabase(...))</appdbcontext>
),并确保每个测试用唯一数据库名(如
Guid.NewGuid().ToString()
若使用真实数据库或外部服务(Redis、RabbitMQ),并发测试前需确认其连接池配置足够,否则瓶颈不在应用层而在下游

真正难排查的并发问题往往不出在

WebApplicationFactory
本身,而在于你没意识到某个
Singleton
服务正被十个请求同时修改同一块内存。

相关推荐