SuperAPI AI 工具集

dotnet-testing-strategy

测试与质量

查看项目

更新于 10-15 01:39

技能介绍

用途

dotnet-testing-strategy 是一个用于决定如何测试 .NET 代码的决策框架技能。它帮助开发者在单元测试、集成测试与端到端(E2E)测试之间做出选择,组织测试项目结构,并挑选合适的测试替身。

该技能明确划定了范围边界:测试项目脚手架(目录布局、xUnit 项目创建、coverlet 配置、editorconfig 覆盖)由 skill:dotnet-add-testing 负责;代码覆盖率工具与变异测试由 skill:dotnet-test-quality 负责;CI 测试报告与流水线集成参见 skill:dotnet-gha-build-test 和 skill:dotnet-ado-build-test。

主要能力

测试类型决策树

从“被测代码是否依赖外部基础设施(数据库、HTTP 服务、文件系统、消息代理)”开始,逐层判断:

  • 依赖外部基础设施且基础设施行为对正确性关键,且需要完整应用栈(中间件、认证、路由)→ E2E / 功能测试(WebApplicationFactory 或 Playwright)
  • 依赖外部基础设施且行为关键但不需要完整栈 → 集成测试(WebApplicationFactory 或 Testcontainers)
  • 依赖外部基础设施但行为不关键 → 使用测试替身的单元测试(mock 基础设施边界)
  • 不依赖外部基础设施且为纯逻辑(计算、转换、验证)→ 单元测试(无需测试替身)
  • 不依赖外部基础设施但非纯逻辑 → 使用测试替身的单元测试(mock 协作者接口)

各测试类型的对比标准

  • 单元测试:无基础设施(mock/fake),每个测试 <10ms,范围是单个类/方法,适用于纯逻辑、领域规则、值对象、转换、验证器。
  • 集成测试:真实基础设施(数据库、HTTP),每个测试 100ms-5s,范围是多个组件,适用于仓储查询、API 契约验证、序列化往返、中间件行为。
  • E2E / 功能测试:完整栈,每个测试 1-30s,范围是整个请求管道,适用于关键用户流程、认证+路由+中间件组合、横切关注点验证。

成本收益指导

  • 业务逻辑优先使用单元测试:运行快、定位失败精确、无基础设施要求。
  • 用集成测试验证基础设施边界是否正确工作。使用 mock 的 DbContext 做仓储单元测试无法证明实际 SQL 生成,应通过 Testcontainers 使用真实数据库。
  • E2E 测试仅用于关键路径,应谨慎使用。它们慢、脆弱、维护成本高。覆盖正常路径和一两个关键失败场景即可。
  • 测试金字塔是指导而非规则。某些应用(逻辑极少的 CRUD API)可能受益于比单元测试更多的集成测试。策略应匹配应用的复杂度特征。

测试组织

项目命名约定:在 tests/ 下镜像 src/ 的项目结构,并加后缀标明测试类型:

MyApp/
  src/
    MyApp.Domain/
    MyApp.Application/
    MyApp.Api/
    MyApp.Infrastructure/
  tests/
    MyApp.Domain.UnitTests/
    MyApp.Application.UnitTests/
    MyApp.Api.IntegrationTests/
    MyApp.Api.FunctionalTests/
    MyApp.Infrastructure.IntegrationTests/
  • *.UnitTests:隔离测试,无外部依赖
  • *.IntegrationTests:真实基础设施(数据库、HTTP、文件系统)
  • *.FunctionalTests:通过 WebApplicationFactory 的完整应用栈

测试类组织:每个生产类对应一个测试类,测试文件放在镜像生产命名空间的命名空间中。大型生产类可按方法拆分测试类(如 OrderService_CreateTests.cs、OrderService_CancelTests.cs、OrderService_RefundTests.cs)。

测试命名约定

使用 Method_Scenario_ExpectedBehavior 模式,例如:

public class OrderServiceTests
{
    [Fact]
    public void CalculateTotal_WithDiscountCode_AppliesPercentageDiscount() { }

    [Fact]
    public void CalculateTotal_WithExpiredDiscount_ThrowsInvalidOperationException() { }

    [Fact]
    public async Task SubmitOrder_WhenInventoryInsufficient_ReturnsOutOfStockError() { }
}

备选命名风格(每个项目选一种并保持一致):Method_Scenario_Expected、Should_Expected_When_Scenario、Given_When_Then。

Arrange-Act-Assert 模式

每个测试遵循 AAA 结构,清晰分隔三个部分。如果无法清晰标注三个部分,测试可能做得太多,应拆分为多个测试。

测试替身选择

  • Stub:返回预设数据,不做状态验证,用于需要依赖返回特定值以便被测代码继续执行。
  • Mock:验证交互,做交互验证,用于需要验证被测代码以特定方式调用了依赖。
  • Fake:可工作的实现,做状态验证,用于需要轻量但功能性的替代品(内存仓储、内存消息总线)。
  • Spy:记录调用供后续断言,做交互验证,用于需要验证调用发生但不想预先规定。

决策指导:需要验证依赖如何被调用?是→还需要可工作的实现?是→Spy,否→Mock。否→需要依赖做真实的事情?是→Fake,否→Stub。

何时优先使用 Fake 而非 Mock

  • 领域密集型应用:Fake 为复杂交互提供更真实的行为。内存仓储能捕获 mock 遗漏的 bug(如重复键冲突)。
  • 过度使用 mock 是测试坏味道。如果测试的 mock 设置比实际断言还多,考虑 fake 是否更清晰、更易维护。
  • 集成边界更适合通过真实基础设施测试,而非 mock。mock 的 DbContext 无法验证 LINQ 是否转换为有效 SQL。

测试反模式

  1. 测试实现细节:应测试可观察结果,而非内部方法调用序列。
  2. 过度 mock 设置:mock 设置比实际测试还长时,使用 builder 或 fake 封装设置。
  3. 非确定性测试:测试不得依赖系统时钟、随机值或外部网络,应注入抽象。使用 TimeProvider(.NET 8+)而非直接使用 DateTime.UtcNow。

关键原则

  • 测试行为而非实现:断言可观察结果(返回值、状态变化、发布的事件),而非内部方法调用。
  • 每个测试一个逻辑断言:多个 Assert 调用若验证同一逻辑概念可以接受;多个不相关断言说明应拆分测试。
  • 保持测试独立:任何测试不应依赖另一测试的执行或顺序,每个测试使用全新 fixture。
  • 命名使失败自文档化:失败的测试名应无需阅读测试体就能说明哪里坏了。
  • 测试类型匹配风险:高风险代码(支付、认证)值得集成和 E2E 覆盖;低风险代码(简单映射)只需单元测试。
  • 时间相关逻辑使用 TimeProvider(.NET 8+):这是框架提供的抽象,不要创建自定义 IClock 接口。

代理注意事项

  • 不要 mock 你不拥有的类型。mock HttpClient、DbContext 或框架类型会导致脆弱测试,无法反映真实行为。应使用 WebApplicationFactory 或 Testcontainers。
  • 不要在未检查现有结构的情况下创建测试项目。先运行 skill:dotnet-project-analysis,重复测试基础设施会导致构建冲突。
  • 不要在测试中使用 Thread.Sleep。使用带取消令牌的 Task.Delay,或更好的是使用 FakeTimeProvider.Advance() 确定性地控制时间。
  • 不要直接测试私有方法。如果私有方法需要自己的测试,应提取到自己的类中,通过公共 API 测试。
  • 不要在集成测试中硬编码连接字符串。使用 Testcontainers 提供一次性基础设施,或使用 WebApplicationFactory 进行进程内测试。

使用方式

该技能以决策框架和指导文档的形式提供。使用前建议先运行 skill:dotnet-project-analysis 了解解决方案结构,再设计测试策略。相关交叉引用包括:skill:dotnet-xunit(xUnit v3 测试框架特性)、skill:dotnet-integration-testing(WebApplicationFactory 与 Testcontainers 模式)、skill:dotnet-snapshot-testing(基于 Verify 的审批测试)、skill:dotnet-test-quality(覆盖率与变异测试)、skill:dotnet-add-testing(测试项目脚手架)。

适用场景

适用于 .NET 应用开发中需要制定测试策略、选择测试类型、组织测试项目、选择测试替身以及规避测试反模式的场景。

限制

该技能不涵盖测试项目脚手架搭建、代码覆盖率工具与变异测试、CI 测试报告与流水线集成,这些内容由其他相关技能负责。