You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

单元测试中异步/同步函数的使用差异及测试异步方法的最佳实践咨询

单元测试中异步/同步函数的使用差异及测试异步方法的最佳实践咨询

嘿,我特别理解你想在单元测试里精简冗余异步代码的需求——20个重复的async/await堆在一起确实挺烦人的。咱们一个个拆解你提到的几种写法的差异,帮你找到最靠谱的精简方案:


先明确核心原则:单元测试框架对异步代码的处理逻辑

所有主流.NET测试框架(xUnit、NUnit、MSTest)都只会等待返回Task/Task<T>的测试方法完成,而返回void的测试方法会被当成纯同步方法,框架不会等待任何内部的异步操作。这是所有问题的根源。


三种写法的逐一分析

1. 标准异步测试写法(最稳妥,语义清晰)

public async Task TestServiceError()
{
    var client = GetMockClient("{RETURN}", HttpStatusCode.BadRequest);
    await Should.ThrowAsync<PreorderException>(async () => await client.Index());
}

这是官方推荐的标准写法:

  • 测试方法标记async Task,框架会自动等待所有异步操作完成
  • async () => await client.Index()是明确的异步委托,语义清晰,任何人看了都知道这里在等待异步操作
  • 完全不会有异步测试的坑,适合新手或者需要高可读性的场景

2. 返回void的同步测试写法(绝对不能用!)

public void TestServiceError()
{
    var client = GetMockClient("{RETURN}", HttpStatusCode.BadRequest);
    Should.Throw<PreorderException>(async () => await client.Index());
}

这是踩坑重灾区

  • 测试方法返回void,框架会直接执行完方法体就结束测试,完全不会等待内部的异步委托完成
  • 异步委托里的异常可能在测试结束后才抛出,框架根本捕获不到,会变成未处理异常,甚至导致测试进程崩溃
  • 偶尔会出现“测试通过但实际有问题”的幽灵失败,完全不可靠,别碰!

3. 返回Task的无async测试写法(最优精简方案)

public Task TestServiceError()
{
    var client = GetMockClient("{RETURN}", HttpStatusCode.BadRequest);
    return Should.ThrowAsync<PreorderException>(() => client.Index());
}

这就是你要找的既精简又靠谱的写法!原因如下:

  • 测试方法返回Task,框架会自动等待这个Task完成,不需要加async/awaitasync只是语法糖,底层还是返回Task
  • Should.ThrowAsync本身就接受Func<Task>参数,而client.Index()直接返回Task<string>,所以直接传() => client.Index()就够了——FluentAssertions会自己等待这个Task完成,然后检查是否抛出了指定的异常
  • 完全去掉了冗余的async/await,代码更简洁,同时完全避免了异步测试的坑

给你的最终建议

如果你有20个这样的测试,直接用第三种写法就好,甚至可以进一步抽离复用逻辑:

// 抽离复用的mock客户端创建逻辑
private MyApiClient GetErrorClient() => GetMockClient("{RETURN}", HttpStatusCode.BadRequest);

// 精简后的测试用例
public Task TestServiceError_Index() => Should.ThrowAsync<PreorderException>(() => GetErrorClient().Index());
public Task TestServiceError_FindByODL() => Should.ThrowAsync<PreorderException>(() => GetErrorClient().FindByODLTest("order", "dealer", "lab"));
public Task TestServiceError_Register() => Should.ThrowAsync<PreorderException>(() => GetErrorClient().RegisterAsync(new()));

这种写法既保证了测试的可靠性,又把冗余的async/await全部去掉,完全符合你的需求。

内容来源于stack exchange

火山引擎 最新活动