贝利信息

c# 如何对c#异步代码进行单元测试和集成测试

日期:2026-01-13 00:00 / 作者:星降
必须将测试方

法声明为 async Task 而非 async void,await 被测异步方法,禁用 .Result/.Wait();模拟异步依赖需用 Moq ReturnsAsync 或内存数据库;async void 会导致测试框架无法等待和捕获异常。

如何用 xUnit/NUnit 测试 async Task 方法

直接在测试方法里调用 async 方法但不 await,会导致测试提前结束、断言失效或 Task 未完成就退出。必须让测试框架理解这是异步操作。

public class CalculatorServiceTests
{
    [Fact]
    public async Task AddAsync_ReturnsCorrectSum()
    {
        var service = new CalculatorService();
        var result = await service.AddAsync(2, 3); // ✅ 正确:await Task
        Assert.Equal(5, result);
    }
}

如何模拟异步依赖(如 HttpClient、DbContext)

真实 I/O(网络、数据库)不能进单元测试,必须隔离。难点在于:很多异步接口(如 IHttpClientFactoryIDbContext)本身不提供可 mock 的异步虚方法,需借助包装或接口抽象。

集成测试中运行真实异步数据库操作

用 SQLite In-Memory 或 SQL Server LocalDB 可跑带 SaveChangesAsync() 的端到端流程,但要注意生命周期和事务隔离。

var options = new DbContextOptionsBuilder()
    .UseSqlite("Data Source=:memory:")
    .Options;

using var context = new AppDbContext(options);
context.Database.OpenConnection();
context.Database.CreateTables(); // 自定义扩展方法建表
await context.SaveChangesAsync(); // ✅ 可以测真实 async DB 操作

为什么 async void 测试方法一定会失败

async void 是“即发即弃”(fire-and-forget),测试框架无法挂起等待,会立刻判定测试通过,哪怕内部抛了异常也捕获不到。

真正容易被忽略的是:第三方库(比如某些旧版测试辅助类)可能悄悄把回调转成 async void,一旦出现“测试绿了但其实没跑完”,先检查所有 async 方法是否都严格返回 Task