dotnet-add-testing
npx skills add https://github.com/novotnyllc/dotnet-artisan --skill dotnet-add-testing
Agent 安装分布
Skill 文档
dotnet-add-testing
Add test infrastructure scaffolding to an existing .NET project. Creates test projects with xUnit, configures code coverage with coverlet, and sets up the conventional directory structure.
Scope
- Test project creation with xUnit and coverlet
- Conventional directory structure (tests/ mirroring src/)
- Project reference wiring and test SDK configuration
Out of scope
- In-depth testing patterns (xUnit v3, WebApplicationFactory, UI testing) — see [skill:dotnet-testing-strategy]
Prerequisites: Run [skill:dotnet-version-detection] first to determine SDK version and TFM. Run [skill:dotnet-project-analysis] to understand existing solution structure.
Cross-references: [skill:dotnet-project-structure] for overall solution layout conventions, [skill:dotnet-scaffold-project] which includes test scaffolding in new projects, [skill:dotnet-add-analyzers] for test-specific analyzer suppressions.
Test Project Structure
Follow the convention of mirroring src/ project names under tests/:
MyApp/
âââ src/
â âââ MyApp.Core/
â âââ MyApp.Api/
â âââ MyApp.Infrastructure/
âââ tests/
âââ MyApp.Core.UnitTests/
âââ MyApp.Api.UnitTests/
âââ MyApp.Api.IntegrationTests/
âââ Directory.Build.props # Test-specific build settings
Naming conventions:
*.UnitTests— isolated tests with no external dependencies*.IntegrationTests— tests that use real infrastructure (database, HTTP, file system)*.FunctionalTests— end-to-end tests through the full application stack
Step 1: Create the Test Project
# Create xUnit test project
dotnet new xunit -n MyApp.Core.UnitTests -o tests/MyApp.Core.UnitTests
# Add to solution
dotnet sln add tests/MyApp.Core.UnitTests/MyApp.Core.UnitTests.csproj
# Add reference to the project under test
dotnet add tests/MyApp.Core.UnitTests/MyApp.Core.UnitTests.csproj \
reference src/MyApp.Core/MyApp.Core.csproj
Clean Up Generated Project
Remove properties already defined in 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>
With CPM, Version attributes are managed in Directory.Packages.props. Remove them from the generated .csproj.
Step 2: Add Test-Specific Build Properties
Create tests/Directory.Build.props to customize settings for all test projects:
<!-- tests/Directory.Build.props -->
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<!-- Use Microsoft.Testing.Platform v2 runner (requires Microsoft.NET.Test.Sdk 17.13+/18.x) -->
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
<!-- Relax strictness for test projects -->
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
This imports the root Directory.Build.props (for shared settings like Nullable, ImplicitUsings, LangVersion) and overrides test-specific properties.
Step 3: Register Test Packages in CPM
Add test package versions to Directory.Packages.props:
<!-- In Directory.Packages.props -->
<ItemGroup>
<!-- Test packages -->
<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>
Optional: Mocking Library
Add a mocking library if the project needs test doubles:
<PackageVersion Include="NSubstitute" Version="5.3.0" />
Or for assertion libraries:
<PackageVersion Include="FluentAssertions" Version="8.0.1" />
Step 4: Configure Code Coverage
Coverlet (Collector Mode)
The coverlet.collector package integrates with dotnet test via the data collector. No additional configuration is needed for basic coverage.
Generate coverage reports:
# Collect coverage (Cobertura format by default)
dotnet test --collect:"XPlat Code Coverage"
# Results appear in TestResults/*/coverage.cobertura.xml
Coverage Thresholds
For CI enforcement, use coverlet.msbuild for threshold checks:
<!-- In test csproj or tests/Directory.Build.props -->
<PackageReference Include="coverlet.msbuild" />
# Enforce minimum coverage threshold
dotnet test /p:CollectCoverage=true \
/p:CoverageOutputFormat=cobertura \
/p:Threshold=80 \
/p:ThresholdType=line
Coverage Report Generation
Use reportgenerator for human-readable HTML reports:
# Install globally
dotnet tool install -g dotnet-reportgenerator-globaltool
# Generate HTML report
reportgenerator \
-reports:"tests/**/coverage.cobertura.xml" \
-targetdir:coverage-report \
-reporttypes:Html
Step 5: Add EditorConfig Overrides for Tests
In the root .editorconfig, add test-specific relaxations:
[tests/**.cs]
# Allow underscores in test method names (Given_When_Then or Should_Behavior)
dotnet_diagnostic.CA1707.severity = none
# Test parameters are validated by the framework
dotnet_diagnostic.CA1062.severity = none
# ConfigureAwait not relevant in test context
dotnet_diagnostic.CA2007.severity = none
# Tests often have intentionally unused variables for assertions
dotnet_diagnostic.IDE0059.severity = suggestion
Step 6: Write a Starter Test
Replace the template-generated UnitTest1.cs with a properly structured test:
namespace MyApp.Core.UnitTests;
public class SampleServiceTests
{
[Fact]
public void Method_Condition_ExpectedResult()
{
// Arrange
var sut = new SampleService();
// Act
var result = sut.DoWork();
// Assert
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);
}
}
Test Naming Convention
Use the pattern Method_Condition_ExpectedResult:
CreateUser_WithValidInput_ReturnsUserGetById_WhenNotFound_ReturnsNullDelete_WithoutPermission_ThrowsUnauthorized
Verify
After adding test infrastructure, verify everything works:
# Restore (regenerate lock files if using CPM)
dotnet restore
# Build (verifies project references and analyzer config)
dotnet build --no-restore
# Run tests
dotnet test --no-build
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"
Adding Integration Test Projects
For integration tests that need WebApplicationFactory or database access:
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
Add integration test packages to CPM (match the Microsoft.AspNetCore.Mvc.Testing major version to the target framework — e.g., 8.x for net8.0, 9.x for net9.0, 10.x for net10.0):
<!-- Version must match the project's target framework major version -->
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageVersion Include="Testcontainers" Version="4.3.0" />
Integration test depth (WebApplicationFactory patterns, test containers, database fixtures) — see [skill:dotnet-integration-testing].
What’s Next
This skill covers test project scaffolding. For deeper testing guidance:
- xUnit v3 features and patterns — [skill:dotnet-xunit]
- Integration testing with WebApplicationFactory — [skill:dotnet-integration-testing]
- UI testing (Blazor, MAUI, Uno) — [skill:dotnet-blazor-testing], [skill:dotnet-maui-testing], [skill:dotnet-uno-testing]
- Snapshot testing — [skill:dotnet-snapshot-testing]
- Test quality and coverage enforcement — [skill:dotnet-test-quality]
- CI test reporting — [skill:dotnet-add-ci] for starter, [skill:dotnet-gha-build-test] and [skill:dotnet-ado-build-test] for advanced