Token导航 LogoToken导航TokenDH.com
研究检索external-servicegithub未标认证来源可访问许可证需确认审计通过

dotnet-test-quality点网测试质量

Agent Skill

用于辅助测试设计、自动化测试、用例整理和回归验证。它适合让 Agent 编写单元测试、端到端测试、测试计划或根据失败日志定位问题。使用时需要确认项目测试框架、运行命令和夹具数据,避免为了通过测试而改坏真实逻辑;涉及浏览器或外部服务时,应区分本地模拟、测试环境和生产环境。

总安装

329

周安装

14

GitHub Stars

15

下载量

115
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:dotnet-test-quality(点网测试质量)
来源仓库:https://github.com/wshaddix/dotnet-skills
仓库路径:skills/dotnet-test-quality
安装命令:
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-test-quality
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-test-quality

简介

dotnet-test-quality 分析 .NET 项目的测试质量,包括覆盖率收集、突变测试与 Flaky 检测。

  • 适用于评估测试有效性、识别低效代码区域及优化回归策略。
  • 集成 coverlet、ReportGenerator 和 Stryker.NET 等工具链提供专业洞察。
  • 需 .NET 8.0+ 及对应工具版本,建议在 CI 环境中运行以获取准确指标。
  • 输出结果应结合业务上下文解读,避免唯数字论导致的无效优化。

SKILL.md

dotnet-test-quality

Test quality analysis for.NET projects. Covers code coverage collection with coverlet, human-readable coverage reports with ReportGenerator, CRAP (Change Risk Anti-Patterns) score analysis to identify undertested complex code, mutation testing with Stryker.NET to evaluate test suite effectiveness, and strategies for detecting and managing flaky tests.

Version assumptions: Coverlet 6.x+, ReportGenerator 5.x+, Stryker.NET 4.x+ (.NET 8.0+ baseline). Coverlet supports both the MSBuild integration (coverlet.msbuild) and the coverlet.collector data collector; examples use coverlet.collector as the recommended approach.

Out of scope: Test project scaffolding (creating projects, package references, coverlet setup) is owned by [skill:dotnet-add-testing]. Testing strategy and test type decisions are covered by [skill:dotnet-testing-strategy]. CI test reporting and pipeline integration -- see [skill:dotnet-gha-build-test] and [skill:dotnet-ado-build-test].

Prerequisites: Test project already scaffolded via [skill:dotnet-add-testing] with coverlet packages referenced..NET 8.0+ baseline required.

Cross-references: [skill:dotnet-testing-strategy] for deciding what to test and coverage target guidance, [skill:dotnet-xunit] for xUnit test framework features and configuration.


Code Coverage with Coverlet

Coverlet is the standard open-source code coverage library for.NET. It instruments assemblies at build time or via a data collector and produces coverage reports in multiple formats.

Packages

<!-- Data collector approach (recommended) -->
<PackageReference Include="coverlet.collector" Version="8.0.0">
  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  <PrivateAssets>all</PrivateAssets>
</PackageReference>

Collecting Coverage

# Collect coverage with Cobertura output (default for ReportGenerator)
dotnet test --collect:"XPlat Code Coverage"

# Specify output format explicitly
dotnet test --collect:"XPlat Code Coverage" \
  -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura

# Multiple formats
dotnet test --collect:"XPlat Code Coverage" \
  -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura,opencover

Coverage results are written to TestResults/<guid>/coverage.cobertura.xml under each test project's output directory.

Filtering Coverage

Exclude generated code, test projects, or specific namespaces:

dotnet test --collect:"XPlat Code Coverage" \
  -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Exclude="[*.Tests]*,[*.IntegrationTests]*" \
  DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute="GeneratedCodeAttribute,ObsoleteAttribute,ExcludeFromCodeCoverageAttribute"

Or configure via a runsettings file for repeatability:

<!-- coverlet.runsettings -->
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="XPlat Code Coverage">
        <Configuration>
          <Format>cobertura</Format>
          <Exclude>[*.Tests]*,[*.IntegrationTests]*</Exclude>
          <ExcludeByAttribute>
            GeneratedCodeAttribute,ObsoleteAttribute,ExcludeFromCodeCoverageAttribute
          </ExcludeByAttribute>
          <ExcludeByFile>**/Migrations/**</ExcludeByFile>
          <IncludeTestAssembly>false</IncludeTestAssembly>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>
dotnet test --settings coverlet.runsettings

Merge Coverage from Multiple Test Projects

When a solution has multiple test projects, merge their coverage into a single report:

# Run all tests, collecting coverage per project
dotnet test --collect:"XPlat Code Coverage"

# Find all coverage files and merge via ReportGenerator (see next section)

Coverage Reports with ReportGenerator

ReportGenerator converts raw coverage data (Cobertura, OpenCover) into human-readable HTML reports with line-level highlighting.

Installation

# Install as a global tool
dotnet tool install -g dotnet-reportgenerator-globaltool

# Or as a local tool
dotnet tool install dotnet-reportgenerator-globaltool

Generating Reports

# Single coverage file
reportgenerator \
  -reports:"tests/MyApp.Tests/TestResults/*/coverage.cobertura.xml" \
  -targetdir:"coverage-report" \
  -reporttypes:"Html;TextSummary"

# Multiple test projects (glob pattern merges automatically)
reportgenerator \
  -reports:"**/TestResults/*/coverage.cobertura.xml" \
  -targetdir:"coverage-report" \
  -reporttypes:"Html;Cobertura;TextSummary"

Report Types

TypeDescriptionUse Case
HtmlInteractive HTML with line highlightingLocal developer review
HtmlInline_AzurePipelinesHTML optimized for Azure DevOpsCI artifact
CoberturaMerged Cobertura XMLInput for other tools
TextSummaryPlain text summaryCLI/CI output
BadgesSVG coverage badgesREADME badges
MarkdownSummaryGithubGitHub-flavored markdownPR comments

Example: Full Coverage Pipeline

#!/bin/bash
# clean previous results
rm -rf coverage-report TestResults

# run tests with coverage
dotnet test --collect:"XPlat Code Coverage" --results-directory TestResults

# generate merged HTML report
reportgenerator \
  -reports:"**/TestResults/*/coverage.cobertura.xml" \
  -targetdir:"coverage-report" \
  -reporttypes:"Html;TextSummary;Badges"

# display summary
cat coverage-report/Summary.txt

Setting Coverage Thresholds

Enforce minimum coverage in CI by parsing the text summary or using a threshold parameter:

# ReportGenerator does not enforce thresholds directly.
# Parse the summary or use dotnet-coverage (Microsoft) for threshold enforcement.

# Alternative: use coverlet's built-in threshold via MSBuild
dotnet test /p:CollectCoverage=true \
  /p:Threshold=80 \
  /p:ThresholdType=line \
  /p:ThresholdStat=total

Note: The /p:Threshold parameter requires the coverlet.msbuild package (not coverlet.collector). For coverlet.collector workflows, enforce thresholds by parsing the ReportGenerator text summary in your CI script.


CRAP Analysis

CRAP (Change Risk Anti-Patterns) scores identify methods that are both complex and poorly tested. A high CRAP score means the method has high cyclomatic complexity and low code coverage -- a risky combination.

Formula

CRAP(m) = complexity(m)^2 * (1 - coverage(m)/100)^3 + complexity(m)

Where:

  • complexity(m) = cyclomatic complexity of method m
  • coverage(m) = code coverage percentage of method m (0-100)

Interpreting CRAP Scores

CRAP ScoreRisk LevelAction
< 5LowMethod is simple or well-tested
5-15ModerateReview -- may need additional tests
15-30HighPrioritize: add tests or reduce complexity
> 30CriticalRefactor and add tests immediately

Generating CRAP Reports

ReportGenerator includes CRAP analysis when using OpenCover format as input:

# Step 1: Collect coverage in OpenCover format
dotnet test --collect:"XPlat Code Coverage" \
  -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover

# Step 2: Generate report with risk hotspot analysis
reportgenerator \
  -reports:"**/TestResults/*/coverage.opencover.xml" \
  -targetdir:"coverage-report" \
  -reporttypes:"Html;RiskHotspots"

The Risk Hotspots report highlights methods sorted by CRAP score, showing:

  • Method name and containing class
  • Cyclomatic complexity
  • Code coverage percentage
  • Computed CRAP score

Using CRAP Scores Effectively

// Example: a method with high complexity and low coverage
// Cyclomatic complexity: 12, Coverage: 20%
// CRAP = 12^2 * (1 - 0.20)^3 + 12 = 144 * 0.512 + 12 = 85.7 (Critical)
public decimal CalculateShipping(Order order)
{
    if (order.Items.Count == 0) return 0;

    decimal baseRate = order.DestinationCountry switch
    {
        "US" => 5.99m,
        "CA" => 9.99m,
        "UK" => 12.99m,
        _ => 19.99m
    };

    if (order.Total > 100) baseRate *= 0.5m;
    if (order.IsPriority) baseRate *= 2.0m;
    if (order.Items.Any(i => i.IsFragile)) baseRate += 4.99m;
    if (order.Items.Any(i => i.IsOversized)) baseRate += 14.99m;
    if (order.HasInsurance) baseRate += order.Total * 0.02m;
    if (order.IsExpedited && order.DestinationCountry != "US") baseRate *= 1.5m;

    return Math.Round(baseRate, 2);
}

Address high CRAP scores by:

  1. Adding targeted tests for uncovered branches to reduce the score via higher coverage
  2. Reducing complexity by extracting methods (e.g., separate CalculateBaseRate and ApplySurcharges methods)
  3. Both -- the most effective approach combines better coverage with simpler methods

Mutation Testing with Stryker.NET

Mutation testing evaluates test suite quality by introducing small changes (mutations) to production code and checking whether tests detect them. If a mutation survives (tests still pass), the test suite has a gap.

Installation

# Install as a global tool
dotnet tool install -g dotnet-stryker

# Or as a local tool (recommended for team consistency)
dotnet tool install dotnet-stryker

Running Stryker.NET

# From the test project directory
cd tests/MyApp.Tests
dotnet stryker

# Specify the source project explicitly
dotnet stryker --project MyApp.csproj

# Target specific files
dotnet stryker --mutate "src/Services/**/*.cs"

Configuration File

Create stryker-config.json in the test project directory:

{
  "$schema": "https://raw.githubusercontent.com/stryker-mutator/stryker-net/master/src/Stryker.Core/Stryker.Core/stryker-config.schema.json",
  "stryker-config": {
    "project": "MyApp.csproj",
    "reporters": ["html", "progress", "cleartext"],
    "mutation-level": "Standard",
    "thresholds": {
      "high": 80,
      "low": 60,
      "break": 50
    },
    "mutate": [
      "src/Services/**/*.cs",
      "!src/Services/Migrations/**/*.cs"
    ],
    "ignore-mutations": [
      "string",
      "linq"
    ]
  }
}

Understanding Mutation Results

Stryker reports mutations in four categories:

StatusMeaningAction
KilledA test detected the mutation (failed)Good -- test suite caught the defect
SurvivedNo test detected the mutation (all passed)Gap -- add or strengthen tests
No CoverageNo test covers the mutated codeGap -- add tests for this code
TimeoutMutation caused an infinite loop or timeoutUsually killed (counts as detected)

Mutation Score

Mutation Score = Killed / (Killed + Survived + NoCoverage) * 100

A mutation score of 80%+ indicates a strong test suite. Below 60% suggests significant gaps.

Example: Identifying Test Gaps

Given this production code:

public class PricingService
{
    public decimal CalculateDiscount(decimal price, CustomerTier tier) =>
        tier switch
        {
            CustomerTier.Bronze => price * 0.05m,
            CustomerTier.Silver => price * 0.10m,
            CustomerTier.Gold => price * 0.15m,
            CustomerTier.Platinum => price * 0.20m,
            _ => 0m
        };
}

If tests only verify Gold tier, Stryker generates mutations like:

  • Replace 0.05m with 0.06m (survived -- no Bronze test)
  • Replace 0.10m with 0.11m (survived -- no Silver test)
  • Replace 0.15m with 0.16m (killed -- Gold test catches this)
  • Replace 0.20m with 0.21m (survived -- no Platinum test)
  • Replace 0m with 1m (survived -- no default test)

The HTML report highlights each surviving mutation with the exact code change, guiding where to add tests.

Stryker Thresholds

{
  "thresholds": {
    "high": 80,   // Green: mutation score >= 80%
    "low": 60,    // Yellow: 60% <= mutation score < 80%
    "break": 50   // Red: mutation score < 50% -> exit code 1
  }
}

The break threshold causes Stryker to return a non-zero exit code, useful for CI gates.


Flaky Test Detection

Flaky tests pass and fail intermittently without code changes. They erode trust in the test suite and slow development.

Common Causes

CauseSymptomFix
Shared mutable stateTests fail when run in specific orderUse proper test isolation (see [skill:dotnet-xunit] for fixtures)
Time-dependent logicTests fail near midnight or at specific timesInject TimeProvider (or ISystemClock) instead of using DateTime.Now
Race conditionsTests fail intermittently under parallel executionUse ICollectionFixture for shared resources; avoid shared static state
External dependenciesTests fail when network/services unavailableMock external calls; use Testcontainers for infrastructure
Port conflictsTests fail when another process uses the same portUse dynamic port allocation (WebApplicationFactory handles this)
File system contentionTests fail under parallel executionUse unique temp directories per test (see [skill:dotnet-xunit] IAsyncLifetime patterns)

Detecting Flaky Tests

Repeated Runs

# Run tests multiple times to surface flakiness
for i in $(seq 1 10); do
  dotnet test --logger "trx;LogFileName=run-$i.trx" || echo "Run $i failed"
done

xUnit Conditional Skip

xUnit v3 has built-in conditional skip via Skip on [Fact]:

// xUnit v3 — built-in conditional skip
[Fact(Skip = "Requires external service")]
public async Task ExternalApi_ReturnsData()
{
    var result = await _client.GetDataAsync();
    Assert.NotEmpty(result);
}

// xUnit v3 — runtime skip via Assert.Skip
[Fact]
public async Task ExternalApi_ReturnsData()
{
    if (!await IsServiceAvailable())
        Assert.Skip("External service unavailable");

    var result = await _client.GetDataAsync();
    Assert.NotEmpty(result);
}

Time-Dependent Tests

Replace DateTime.Now/DateTime.UtcNow with.NET 8's TimeProvider:

// Production code
public class SubscriptionService(TimeProvider timeProvider)
{
    public bool IsExpired(Subscription sub)
    {
        var now = timeProvider.GetUtcNow();
        return sub.ExpiresAt < now;
    }
}

// Test code
[Fact]
public void IsExpired_PastExpiry_ReturnsTrue()
{
    var fakeTime = new FakeTimeProvider(
        new DateTimeOffset(2025, 6, 15, 0, 0, 0, TimeSpan.Zero));

    var service = new SubscriptionService(fakeTime);
    var sub = new Subscription
    {
        ExpiresAt = new DateTimeOffset(2025, 6, 14, 0, 0, 0, TimeSpan.Zero)
    };

    Assert.True(service.IsExpired(sub));
}

[Fact]
public void IsExpired_FutureExpiry_ReturnsFalse()
{
    var fakeTime = new FakeTimeProvider(
        new DateTimeOffset(2025, 6, 15, 0, 0, 0, TimeSpan.Zero));

    var service = new SubscriptionService(fakeTime);
    var sub = new Subscription
    {
        ExpiresAt = new DateTimeOffset(2025, 6, 16, 0, 0, 0, TimeSpan.Zero)
    };

    Assert.False(service.IsExpired(sub));
}

Note: FakeTimeProvider is available in Microsoft.Extensions.TimeProvider.Testing (NuGet).

Quarantine Strategy

When a flaky test cannot be fixed immediately:

// Mark as skipped with a tracking issue
[Fact(Skip = "Flaky: tracking in #1234 -- race condition in event handler")]
public async Task EventHandler_ConcurrentEvents_ProcessesAll()
{
    // ...
}

Do not delete flaky tests. Skip them with an issue reference and fix them systematically.


Key Principles

  • Coverage is a lagging indicator, not a target. High coverage does not guarantee good tests. A test suite with 90% coverage can still miss critical bugs if the assertions are weak.
  • Use CRAP scores to prioritize. Focus testing effort on methods with high complexity and low coverage rather than chasing overall coverage percentage.
  • Run mutation testing on critical paths. Mutation testing is computationally expensive. Focus on business-critical code (pricing, authentication, data validation) rather than running it on the entire codebase.
  • Fix flaky tests immediately or quarantine them. A flaky test that remains in the suite trains developers to ignore failures, undermining the entire test suite's value.
  • Measure trends, not snapshots. Track coverage and mutation scores over time. A declining trend indicates test quality erosion even if absolute numbers look acceptable.
  • Exclude generated code from coverage. Migrations, generated clients, and scaffolded code inflate or deflate coverage numbers without reflecting actual test quality.

Agent Gotchas

  1. Do not confuse coverlet.collector with coverlet.msbuild. The coverlet.collector package uses the --collect:"XPlat Code Coverage" CLI flag. The coverlet.msbuild package uses /p:CollectCoverage=true MSBuild properties. Do not mix flags across packages -- they are independent integration points.
  2. Do not hardcode coverage result paths. The GUID in TestResults/<guid>/coverage.cobertura.xml changes every run. Always use glob patterns (**/TestResults/*/coverage.cobertura.xml) when referencing coverage output files.
  3. Do not set coverage thresholds too high initially. Starting with 90%+ thresholds on an existing project blocks all PRs. Begin with the current baseline and increase incrementally (e.g., 5% per quarter).
  4. Do not run Stryker.NET on the entire solution for CI. Mutation testing is CPU-intensive. In CI, limit mutations to changed files (--since:main) or critical paths. Reserve full runs for nightly builds.
  5. Do not ignore survived mutations in trivial code. While some survived mutations are in code that does not warrant testing (logging, ToString()), review each one. Configure ignore-mutations in stryker-config.json for categories you have consciously decided not to test.
  6. Do not use [ExcludeFromCodeCoverage] as a blanket fix for low coverage. This attribute hides the problem rather than solving it. Use it only for genuinely untestable code (platform interop, generated code) and ensure the reason is documented.

References

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

37.43%
按下载量换算43

Claude

29.01%
按下载量换算33

Cursor

20.04%
按下载量换算23

Gemini CLI

9.27%
按下载量换算11

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

external-service

该 Skill 可能调用第三方服务、云服务或外部模型 API,使用前需要确认账号、额度、数据发送范围和服务条款。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills