如何用NUnit在Parallel.For中测试gRPC客户端并发请求?
多用户并发请求服务器的测试问题与解决方法
问题根源
你当前的代码核心问题是**Parallel.For不支持异步委托**:
Parallel.For是为同步CPU密集型任务设计的,当你传入async委托时,它会将其视为async void处理——也就是不会等待异步任务完成,测试方法会直接执行到末尾并结束,导致服务器连接被客户端主动关闭(对应你看到的服务器日志)。- 异步委托中抛出的异常也无法被外层的
try/catch捕获,因为async void的异常会直接抛到线程池,不会回到测试方法的调用栈,所以你看不到异常但实际请求可能已经失败。
正确的解决方案
方法1:用Task.WhenAll实现异步并发
这是异步场景下实现并发的标准方式,同时NUnit支持异步测试方法(返回Task即可):
[Test] public async Task Agregar2RegistrosHorariosDeEntradaDe2EmpleadosAsync() { try { // 生成2个异步登录任务 var loginTasks = Enumerable.Range(0, 2).Select(async _ => { await miClienteLogin.LoginAsync(_nombreUsuario, _password); }); // 等待所有登录任务完成 await Task.WhenAll(loginTasks); } catch (Exception ex) { Assert.Fail($"发生未预期异常:{ex.Message}"); } }
注意:如果要模拟不同用户,需要为每个任务创建独立的客户端实例(避免共享会话或连接状态),比如把miClienteLogin的创建放到Select委托内部。
方法2:控制并发度(适合大量用户场景)
如果要模拟更多用户,同时避免一次性发起过多请求压垮服务器,可以用SemaphoreSlim限制并发数:
[Test] public async Task AgregarMultiplesRegistrosHorariosAsync() { int maxConcurrency = 5; // 同时最多5个请求 int totalUsers = 10; // 模拟10个用户登录 using var semaphore = new SemaphoreSlim(maxConcurrency); var loginTasks = Enumerable.Range(0, totalUsers).Select(async userId => { await semaphore.WaitAsync(); try { // 每个用户用独立的客户端实例 var cliente = new MiClienteLogin(); await cliente.LoginAsync($"usuario_{userId}", "contraseña"); // 这里可以添加断言,比如验证登录是否成功 } finally { semaphore.Release(); } }); await Task.WhenAll(loginTasks); }
方法3:专业负载测试工具(推荐)
如果需要模拟大规模并发(比如数百上千用户),代码测试的方式会有局限,建议使用专业工具:
- k6:用JavaScript编写测试脚本,支持高并发,自带性能指标统计
- Apache JMeter:可视化界面,适合复杂场景的负载测试
- Locust:用Python编写测试逻辑,灵活度高
关键注意事项
- 每个并发请求必须使用独立的客户端实例,避免共享HTTP连接或会话状态,否则会导致请求冲突或不符合真实用户场景。
- 不要只依赖“不抛异常”作为测试通过的条件,要添加断言验证请求结果(比如登录后返回的用户信息是否正确)。
内容的提问来源于stack exchange,提问作者Álvaro García
相关产品推荐
相关产品推荐

