.NET怎么做集成测试 WebApplicationFactory使用教程

来源:这里教程网 时间:2026-02-21 17:34:47 作者:

WebApplicationFactory 是 .NET 集成测试的核心工具,它能启动一个轻量、内存中的测试版 Web 应用,不依赖真实 IIS 或 Kestrel,也不需要端口占用或进程管理,特别适合验证控制器逻辑、中间件行为、依赖注入配置和端到端 HTTP 流程。

一、基础用法:创建并使用 WebApplicationFactory

在测试项目中安装 Microsoft.AspNetCore.Mvc.Testing NuGet 包(.NET 5+ 已内置,但建议显式引用)。

假设你的主项目叫

MyApp.Web
,其程序入口是
Program.cs
(Minimal Hosting Model)或
Startup.cs
(传统模型),则测试类可这样写:

继承
WebApplicationFactory<program></program>
(Minimal 模型)或
WebApplicationFactory<startup></startup>
(传统模型)
通过
CreateClient()
获取
HttpClient
,它自动指向内存中运行的应用
无需手动启动/停止应用,工厂会在测试前后自动处理生命周期

示例:

[Fact]
public async Task GetHome_ReturnsSuccess()
{
    var factory = new WebApplicationFactory<Program>();
    var client = factory.CreateClient();
    var response = await client.GetAsync("/");
    response.EnsureSuccessStatusCode();
}

二、自定义应用配置:重写 CreateWebHostBuilder 或 ConfigureWebHost

默认情况下,

WebApplicationFactory
会复用你主项目的宿主配置。但测试时往往需要替换服务(如用内存数据库代替 SQL Server)、跳过认证、或注入 Mock 对象。

推荐方式是重写

ConfigureWebHost
方法:

调用
builder.ConfigureServices(services => { ... })
替换或新增服务
调用
builder.UseEnvironment("Testing")
切换环境,配合
IWebHostEnvironment
做条件注册
避免修改
Program.cs
主逻辑,所有测试定制都收束在测试类内部

常见替换示例(用

InMemoryDatabase
):

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    builder.ConfigureServices(services =>
    {
        services.RemoveAll(typeof(DbContextOptions<AppDbContext>));
        services.AddDbContext<AppDbContext>(options =>
            options.UseInMemoryDatabase("TestDb"));
    });
}

三、处理认证与用户上下文

如果控制器有 [Authorize] 特性,直接发请求会返回 401。解决方法不是关掉认证,而是模拟已登录用户:

ClaimsPrincipal
构造测试用户,并通过
client.DefaultRequestHeaders.Authorization
设置 Bearer Token(需配合测试 JWT 签发)
更轻量的方式:在
ConfigureWebHost
中注册一个测试用的
IAuthenticationService
或直接替换
IHttpContextAccessor
(不推荐)
推荐做法:使用
AddAuthentication().AddScheme<testauthhandler></testauthhandler>
注册一个仅用于测试的认证方案,在 handler 中直接返回成功 principal

简化版(无 Token 验证,纯模拟):

builder.ConfigureServices(services =>
{
    services.AddAuthentication("Test")
        .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", _ => { });
});
// 在 TestAuthHandler.HandleAuthenticateAsync 中 return AuthenticateResult.Success(ticket);

四、清理与复用:避免测试间干扰

每个

WebApplicationFactory
实例默认是隔离的,但若多个测试共用一个实例(比如用
[Collection]
共享工厂),要注意:

数据库 InMemory 实例默认是静态的,跨测试会残留数据 → 改用带唯一名称的
UseInMemoryDatabase("TestDb_" + Guid.NewGuid())
缓存、静态字段、单例服务状态可能污染后续测试 → 在
Dispose
或每个测试开头重置关键状态
不需要手动调用
Dispose()
,xUnit 的
IClassFixture<t></t>
会自动管理工厂生命周期

推荐结构(共享工厂 + 隔离数据库):

public class IntegrationTest : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    public IntegrationTest(WebApplicationFactory<Program> factory) => _factory = factory;
    // 后续测试方法中每次新建 client 即可
}

基本上就这些。WebApplicationFactory 不复杂但容易忽略环境隔离和依赖替换细节。把配置收在

ConfigureWebHost
里,坚持“测试即真实请求”,集成测试就能稳稳跑起来。

相关推荐