name: dotnet-add-testing description: “向.NET项目添加测试基础设施。搭建xUnit项目、coverlet、布局。”
dotnet-add-testing
向现有.NET项目添加测试基础设施脚手架。创建包含xUnit的测试项目,配置coverlet代码覆盖,并设置常规目录结构。
范围
- 使用xUnit和coverlet创建测试项目
- 常规目录结构(tests/镜像src/)
- 项目引用连接和测试SDK配置
超出范围
- 深入的测试模式(xUnit v3、WebApplicationFactory、UI测试)——参见[skill:dotnet-testing-strategy]
先决条件: 首先运行[skill:dotnet-version-detection]以确定SDK版本和TFM。运行[skill:dotnet-project-analysis]以了解现有解决方案结构。
交叉引用:[skill:dotnet-project-structure]用于整体解决方案布局惯例,[skill:dotnet-scaffold-project]包括新项目中的测试脚手架,[skill:dotnet-add-analyzers]用于测试特定分析器抑制。
测试项目结构
遵循镜像src/项目名称到tests/下的惯例:
MyApp/
├── src/
│ ├── MyApp.Core/
│ ├── MyApp.Api/
│ └── MyApp.Infrastructure/
└── tests/
├── MyApp.Core.UnitTests/
├── MyApp.Api.UnitTests/
├── MyApp.Api.IntegrationTests/
└── Directory.Build.props # 测试特定构建设置
命名惯例:
*.UnitTests—— 无外部依赖的隔离测试*.IntegrationTests—— 使用真实基础设施(数据库、HTTP、文件系统)的测试*.FunctionalTests—— 通过完整应用程序堆栈的端到端测试
步骤1:创建测试项目
# 创建xUnit测试项目
dotnet new xunit -n MyApp.Core.UnitTests -o tests/MyApp.Core.UnitTests
# 添加到解决方案
dotnet sln add tests/MyApp.Core.UnitTests/MyApp.Core.UnitTests.csproj
# 添加对被测项目的引用
dotnet add tests/MyApp.Core.UnitTests/MyApp.Core.UnitTests.csproj \
reference src/MyApp.Core/MyApp.Core.csproj
清理生成的项目
移除已在Directory.Build.props中定义的属性:
<!-- tests/MyApp.Core.UnitTests/MyApp.Core.UnitTests.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit.v3" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="coverlet.collector" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\MyApp.Core\MyApp.Core.csproj" />
</ItemGroup>
</Project>
使用CPM时,Version属性在Directory.Packages.props中管理。从生成的.csproj中移除它们。
步骤2:添加测试特定构建属性
创建tests/Directory.Build.props以自定义所有测试项目的设置:
<!-- tests/Directory.Build.props -->
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<!-- 使用Microsoft.Testing.Platform v2运行器(需要Microsoft.NET.Test.Sdk 17.13+/18.x) -->
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
<!-- 为测试项目放宽严格性 -->
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
这导入根Directory.Build.props(用于共享设置如Nullable、ImplicitUsings、LangVersion)并覆盖测试特定属性。
步骤3:在CPM中注册测试包
向Directory.Packages.props添加测试包版本:
<!-- 在Directory.Packages.props中 -->
<ItemGroup>
<!-- 测试包 -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="coverlet.collector" Version="8.0.0" />
</ItemGroup>
可选:模拟库
如果项目需要测试替身,添加模拟库:
<PackageVersion Include="NSubstitute" Version="5.3.0" />
或用于断言库:
<PackageVersion Include="FluentAssertions" Version="8.0.1" />
步骤4:配置代码覆盖
Coverlet(收集器模式)
coverlet.collector包通过数据收集器与dotnet test集成。基本覆盖无需额外配置。
生成覆盖报告:
# 收集覆盖(默认Cobertura格式)
dotnet test --collect:"XPlat Code Coverage"
# 结果出现在TestResults/*/coverage.cobertura.xml中
覆盖阈值
对于CI强制执行,使用coverlet.msbuild进行阈值检查:
<!-- 在测试csproj或tests/Directory.Build.props中 -->
<PackageReference Include="coverlet.msbuild" />
# 强制执行最小覆盖阈值
dotnet test /p:CollectCoverage=true \
/p:CoverageOutputFormat=cobertura \
/p:Threshold=80 \
/p:ThresholdType=line
覆盖报告生成
使用reportgenerator生成人类可读的HTML报告:
# 全局安装
dotnet tool install -g dotnet-reportgenerator-globaltool
# 生成HTML报告
reportgenerator \
-reports:"tests/**/coverage.cobertura.xml" \
-targetdir:coverage-report \
-reporttypes:Html
步骤5:为测试添加EditorConfig覆盖
在根.editorconfig中,添加测试特定放宽:
[tests/**.cs]
# 允许测试方法名中的下划线(Given_When_Then或Should_Behavior)
dotnet_diagnostic.CA1707.severity = none
# 测试参数由框架验证
dotnet_diagnostic.CA1062.severity = none
# ConfigureAwait在测试上下文中不相关
dotnet_diagnostic.CA2007.severity = none
# 测试中常有意未使用的变量用于断言
dotnet_diagnostic.IDE0059.severity = suggestion
步骤6:编写起始测试
替换模板生成的UnitTest1.cs为适当结构的测试:
namespace MyApp.Core.UnitTests;
public class SampleServiceTests
{
[Fact]
public void Method_Condition_ExpectedResult()
{
// 安排
var sut = new SampleService();
// 行动
var result = sut.DoWork();
// 断言
Assert.NotNull(result);
}
[Theory]
[InlineData(1, 2, 3)]
[InlineData(0, 0, 0)]
[InlineData(-1, 1, 0)]
public void Add_TwoNumbers_ReturnsSum(int a, int b, int expected)
{
var result = Calculator.Add(a, b);
Assert.Equal(expected, result);
}
}
测试命名惯例
使用模式Method_Condition_ExpectedResult:
CreateUser_WithValidInput_ReturnsUserGetById_WhenNotFound_ReturnsNullDelete_WithoutPermission_ThrowsUnauthorized
验证
添加测试基础设施后,验证一切工作:
# 恢复(如果使用CPM,重新生成锁定文件)
dotnet restore
# 构建(验证项目引用和分析器配置)
dotnet build --no-restore
# 运行测试
dotnet test --no-build
# 运行覆盖
dotnet test --collect:"XPlat Code Coverage"
添加集成测试项目
对于需要WebApplicationFactory或数据库访问的集成测试:
dotnet new xunit -n MyApp.Api.IntegrationTests -o tests/MyApp.Api.IntegrationTests
dotnet sln add tests/MyApp.Api.IntegrationTests/MyApp.Api.IntegrationTests.csproj
dotnet add tests/MyApp.Api.IntegrationTests/MyApp.Api.IntegrationTests.csproj \
reference src/MyApp.Api/MyApp.Api.csproj
向CPM添加集成测试包(匹配Microsoft.AspNetCore.Mvc.Testing的主版本到目标框架——例如,net8.0用8.x,net9.0用9.x,net10.0用10.x):
<!-- 版本必须匹配项目的目标框架主版本 -->
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageVersion Include="Testcontainers" Version="4.3.0" />
集成测试深度(WebApplicationFactory模式、测试容器、数据库夹具)——参见[skill:dotnet-integration-testing]。
下一步
本技能涵盖测试项目脚手架。更深测试指南:
- xUnit v3特性和模式 —— [skill:dotnet-xunit]
- 使用WebApplicationFactory进行集成测试 —— [skill:dotnet-integration-testing]
- UI测试(Blazor、MAUI、Uno) —— [skill:dotnet-blazor-testing], [skill:dotnet-maui-testing], [skill:dotnet-uno-testing]
- 快照测试 —— [skill:dotnet-snapshot-testing]
- 测试质量和覆盖强制执行 —— [skill:dotnet-test-quality]
- CI测试报告 —— [skill:dotnet-add-ci]用于起始,[skill:dotnet-gha-build-test]和[skill:dotnet-ado-build-test]用于高级