async方法测试必须用async Task方法声明
同步的
void或
Task测试方法无法正确等待异步操作完成,会导致测试提前结束、断言失效或偶发失败。xUnit/NUnit/MSTest都要求测试方法本身是
async Task,否则
await会被当作同步调用处理。 ✅ 正确写法:
public async Task MyMethod_ShouldReturnExpectedResult()❌ 错误写法:
public void MyMethod_ShouldReturnExpectedResult()或
public Task MyMethod_ShouldReturnExpectedResult()如果用 MSTest,还需在方法上加
[TestMethod];xUnit 则用
[Fact]或
[Theory]
避免直接调用Result或Wait()引发死锁
在测试中对
Task对象调用
.Result或
.Wait(),尤其在有同步上下文(如默认的 UI 线程或 ASP.NET 同步上下文)的测试运行器里,极易触发死锁——因为 await 在捕获上下文后尝试回调回原上下文,而该上下文正被
Wait()阻塞。 永远用
await myAsyncMethod(),而不是
myAsyncMethod().Result若需兼容旧框架(如 .NET Framework 4.6.1 以下),可临时用
ConfigureAwait(false)降低风险,但治标不治本 Mock 异步依赖时,确保返回的是真正可 await 的
Task,比如 Moq 中用
Task.FromResult(…)而非直接 return 值
Mock 异步依赖要用 Task.FromResult 或 ReturnsAsync
测试 async 方法时,常需隔离外部 I/O(如 HTTP 调用、数据库访问)。若 mock 返回普通值而非
Task,运行时会报错“无法将 T 隐式转换为 Task
mock.Setup(x => x.GetAsync()).ReturnsAsync(result)(推荐)或
Task.FromResult(result)NSubstitute 用户:用
substitute.GetAsync().Returns(Task.FromResult(result))注意:不要写
Returns(result),这会让方法签名不匹配,编译可能过,但运行时抛
InvalidCastException
异常断言要用 await + Assert.ThrowsAsync
异步方法抛出的异常被包装在
Task内部,不能用
Assert.ThrowsException<exceptiontype>(() => …)</exceptiontype>这种同步断言方式——它只会捕获同步阶段抛出的异常,而 await 后的异常根本不会触发。 ✅ 正确:
await Assert.ThrowsAsync<invalidoperationexception>(async () => await target.DoWorkAsync())</invalidoperationexception>❌ 错误:
Assert.ThrowsException<invalidoperationexception>(() => target.DoWorkAsync().Wait())</invalidoperationexception>(绕过 await,且易死锁) 部分测试框架(如 xUnit)的
ThrowsAsync是泛型方法,类型参数必须精确匹配,比如
ArgumentException和
ArgumentNullException不等价 实际测试中最容易被忽略的,是测试运行器自身的同步上下文行为——哪怕代码逻辑完全正确,换一个测试发现器(比如从 Visual Studio Test Explorer 切到 dotnet test CLI),也可能因上下文差异导致间歇性失败。务必统一使用
async Task测试方法 +
await+
ThrowsAsync这套组合,别为了“看着快”去碰
Result或
GetAwaiter().GetResult()。
