在ASP.NET Core项目里,单元测试适合验证某个服务类的逻辑,但无法发现中间件顺序错误、路由匹配失败或依赖注入遗漏等问题。集成测试则把整个应用跑起来,通过HTTP请求验证真实行为。C#官方提供的WebApplicationFactory就是做这件事的核心工具,它能在测试进程中启动一个内存版的应用主机,对外暴露HttpClient用于发送请求。

WebApplicationFactory的基本用法
WebApplicationFactory位于Microsoft.AspNetCore.Mvc.Testing包中,泛型参数通常是应用的Program类。测试框架(如xUnit)会在每个测试类里持有该工厂实例,并调用CreateClient方法拿到一个指向内存服务器的HttpClient。这个客户端发出的请求不会走网络,而是由TestServer直接转入应用管道。
下面是一段最基础的集成测试示例,验证根路径返回成功状态:
using System.Net;
using System.Threading.Tasks;
using Xunit;
using Microsoft.AspNetCore.Mvc.Testing;
public class BasicIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public BasicIntegrationTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Fact]
public async Task Get_Root_ReturnsOk()
{
var client = _factory.CreateClient();
var response = await client.GetAsync("/");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
这段代码中,IClassFixture告诉xUnit每个测试共享同一个工厂。CreateClient返回的HttpClient已经配置好基础地址,直接GetAsync即可。相比手动起Kestrel,这种方式启动快、资源占用低,而且测试结束自动回收。
需要注意,如果项目使用了隐式Program(顶级语句),需确保Program类对测试项目可见。可在API项目的csproj里添加<InternalsVisibleTo>让测试程序集访问内部类型,或者把Program改成public partial class。
自定义工厂替换依赖
真实应用常连接数据库或调用外部API,集成测试若直连会产生副作用。通过继承WebApplicationFactory并重写ConfigureWebHost,可以在服务注册完成后替换指定服务,实现依赖隔离。
例如用EF Core的内存数据库替换SQL Server,保证测试不污染生产数据:
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
public class TestFactory<TEntry> : WebApplicationFactory<TEntry> where TEntry : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var desc = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
if (desc != null) services.Remove(desc);
services.AddDbContext<AppDbContext>(opt =>
opt.UseInMemoryDatabase("test_db"));
});
}
}
上面的代码先移除原有的DbContext注册,再添加使用内存库的选项。由于ConfigureWebHost在应用Startup之后执行,所以能覆盖默认实现。测试时注入的AppDbContext就会指向内存实例,每次跑测试可清空重建。
除了数据库,像发邮件、调支付网关的HttpClient也可替换成Mock或自定义Handler。这样既能测完整流程,又不必担心外部系统不稳定导致测试闪烁。
发起请求与断言响应内容
拿到客户端后,除了状态码,还应验证返回体和业务结果。对于JSON接口,可读取内容反序列化后断言字段。WebApplicationFactory默认不会带防伪令牌等中间件限制,但若应用启用了相关过滤,测试客户端需先获取令牌再提交。
以下示例测试创建用户接口并返回了带Id的JSON:
[Fact]
public async Task Post_User_ReturnsCreatedWithId()
{
var client = _factory.CreateClient();
var payload = new StringContent("{"name":"tom"}", System.Text.Encoding.UTF8, "application/json");
var resp = await client.PostAsync("/api/users", payload);
resp.EnsureSuccessStatusCode();
var body = await resp.Content.ReadAsStringAsync();
Assert.Contains(""id"", body);
}
这里用StringContent包装JSON,并设置媒体类型。EnsureSuccessStatusCode会在非2xx时抛异常,让测试快速失败。读取字符串后用Contains粗粒度验证,也可借助System.Text.Json解析成对象做精确比对。
如果接口需要鉴权,可在CreateClient时传入WebApplicationFactoryClientOptions设置AllowAutoRedirect,或在请求头加入测试专用Token。保持测试数据独立、断言明确,集成测试就能长期充当API契约守护者。
常见误区与建议
一个常见错误是在测试里直接new HttpClient而不经过工厂,这样请求根本进不了TestServer。另一个误区是每次测试都新建工厂且不复用,导致启动缓慢。推荐用IClassFixture共享工厂,并在测试间通过唯一数据隔离。
此外,集成测试不应覆盖所有分支,那是单元测试的职责。它更适合验证核心链路:启动正常、关键接口连通、主要中间件生效。把少量高价值场景做成集成测试,配合大量单元测试,才能兼顾速度与信心。
当应用使用健康检查或后台服务时,工厂启动也会触发它们。若后台任务阻塞,可在ConfigureWebHost里移除相关服务,保证测试主机干净退出。合理运用WebApplicationFactory,C#开发者就能用熟悉的语言写出贴近生产的ASP.NET Core集成测试。
WebApplicationFactoryASP.NET_Core集成测试修改时间:2026-08-09 23:48:31