在C#项目里,异步代码随处可见,从接口调用到数据库访问都大量使用async和await。要保证这些逻辑正确,就需要针对异步方法写单元测试和集成测试。测试框架只要支持异步测试用例,就能让测试函数直接返回Task,从而等待被测代码完成。
一、使用xUnit编写异步单元测试
xUnit原生支持异步测试,测试方法声明为public async Task即可。下面是一个验证异步加法逻辑的简单例子:
using System.Threading.Tasks;
using Xunit;
public class Calculator
{
public async Task<int> AddAsync(int a, int b)
{
await Task.Delay(10);
return a + b;
}
}
public class CalculatorTests
{
[Fact]
public async Task AddAsync_ReturnsSum()
{
var calc = new Calculator();
int result = await calc.AddAsync(2, 3);
Assert.Equal(5, result);
}
}
1.1 测试异步异常
当异步方法应该抛异常时,可以用Assert.ThrowsAsync来捕获:
using System;
using System.Threading.Tasks;
using Xunit;
public class Service
{
public async Task<string> GetDataAsync(bool fail)
{
await Task.Delay(10);
if (fail)
throw new InvalidOperationException("bad");
return "ok";
}
}
public class ServiceTests
{
[Fact]
public async Task GetDataAsync_ThrowsWhenFail()
{
var svc = new Service();
await Assert.ThrowsAsync<InvalidOperationException>(() => svc.GetDataAsync(true));
}
}
二、异步集成测试要点
集成测试通常会访问数据库或外部HTTP接口。使用HttpClient或EF Core的异步API时,测试项目里同样以async Task方法驱动流程,并尽量复用同一个容器或测试库。
2.1 调用真实接口的示例
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
public class ApiIntegrationTests
{
[Fact]
public async Task FetchUser_ReturnsContent()
{
var client = new HttpClient();
string url = "https://ipipp.com/api/user/1";
var resp = await client.GetAsync(url);
resp.EnsureSuccessStatusCode();
string body = await resp.Content.ReadAsStringAsync();
Assert.Contains("user", body);
}
}
2.2 避免常见坑
- 不要使用
Task.Result或Wait()阻塞异步代码,容易在上下文同步模型中死锁。 - 测试基类里若使用
IAsyncLifetime,可在InitializeAsync中做异步准备数据。 - 集成测试数据库建议用事务回滚或独立测试库,保证可重复运行。
三、小结
只要测试框架支持返回Task,C#异步代码的单元测试和集成测试写起来并不复杂。核心是把测试方法写成async Task,用await等待被测逻辑,再配合断言验证结果或异常。集成测试中注意异步资源释放与上下文阻塞问题,就能建立可靠的保护网。