# How to Analyze Code Coverage Metrics in .NET Test Projects: The Complete Guide

> Learn to analyze code coverage metrics in .NET test projects. Automate detection, collection, and CRAP score computation to identify high-risk methods needing more tests.

- Repository: [.NET Platform/skills](https://github.com/dotnet/skills)
- Tags: how-to-guide
- Published: 2026-07-07

---

**The `coverage-analysis` skill in the `dotnet/skills` repository automatically detects test projects, instruments them with coverage collectors, and computes CRAP scores to surface high-risk methods that need additional testing.**

The `dotnet/skills` repository provides a turnkey solution when you need to **analyze code coverage metrics in .NET test projects** without manual configuration. Located at `plugins/dotnet-test/skills/coverage-analysis`, this skill orchestrates the entire workflow from solution detection to actionable markdown reports, requiring only the .NET SDK and PowerShell.

## What Is the Coverage-Analysis Skill?

The **coverage-analysis** skill is a structured automation that lives in `plugins/dotnet-test/skills/coverage-analysis` within the `dotnet/skills` repository. It operates entirely from source, discovering your solution structure and test projects automatically (see the Phase 1 script description in [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) lines 80-100). The skill adheres to strict guidelines: it only adds coverage provider packages such as **coverlet.collector** or **Microsoft.Testing.Extensions.CodeCoverage** when missing, never touching source code, and isolates all generated artifacts under `TestResults/coverage-analysis/` (as defined in [`references/guidelines.md`](https://github.com/dotnet/skills/blob/main/references/guidelines.md) lines 3-9).

## The Five-Phase Analysis Workflow

The skill executes a predictable pipeline that transforms raw test execution into risk-based insights.

### Phase 1: Project Discovery

The skill locates the solution or entry project and discovers all associated test projects. It determines the output directory for artifacts and validates the environment before proceeding.

### Phase 2: Test Execution with Coverage

If no Cobertura files exist, the skill ensures a coverage provider is present. It injects `coverlet.collector` or `Microsoft.Testing.Extensions.CodeCoverage` into test projects when needed (logic defined in [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) lines 124-164), then runs `dotnet test` with the appropriate arguments. For Coverlet, it invokes:

```powershell
dotnet test <ENTRY> --collect:"XPlat Code Coverage" ...

```

This generates **Cobertura XML** files that serve as the raw data for all subsequent analysis (see [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) lines 224-230).

### Phase 3: CRAP Score Computation

The skill invokes `scripts/Compute-CrapScores.ps1` to parse the Cobertura XML and calculate per-method **CRAP scores** (Change Risk Anti-Pattern). The script computes the formula:

```powershell
CRAP(m) = comp(m)² * (1 - cov(m))³ + comp(m)

```

This highlights methods that combine high cyclomatic complexity with poor coverage, identifying the most dangerous code to modify without tests (implementation in `Compute-CrapScores.ps1` lines 9-14 and 57-66).

### Phase 4: Method-Level Gap Analysis

Using `scripts/Extract-MethodCoverage.ps1`, the skill extracts line and branch percentages for every method, filtering for uncovered or below-threshold entries. The script outputs `UNCOVERED_METHODS` and `METHODS_FILTERED` JSON structures that pinpoint exactly which files need attention (see `Extract-MethodCoverage.ps1` lines 70-84 and 90-93).

### Phase 5: Optional Report Generation

If requested, the skill installs `dotnet-reportgenerator-globaltool` and creates HTML, Text, or CSV reports using ReportGenerator (configuration in [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) lines 99-106).

## Understanding CRAP Scores and Risk Hotspots

Raw line and branch percentages tell you *what* code executed, but **CRAP scores** reveal *why* coverage may be stuck. A method with high complexity and low coverage receives an elevated CRAP score, flagging it as a risk hotspot. The skill outputs these as a ranked table in [`TestResults/coverage-analysis/coverage-analysis.md`](https://github.com/dotnet/skills/blob/main/TestResults/coverage-analysis/coverage-analysis.md), showing:

- **Overall Line/Branch Coverage** – Aggregated percentages from all Cobertura files (output by `Compute-CrapScores.ps1` lines 57-59).
- **Top-N Risk Hotspots** – Methods with the highest CRAP scores, prioritized for new test writing.
- **Coverage Gaps** – Specific methods falling below configurable line and branch thresholds (default 80% line, 70% branch).

## Running the Analysis Manually

You can execute the skill’s PowerShell scripts directly without the full orchestration.

### Compute Overall Coverage and CRAP Scores

```powershell
$skillDir = "$(git rev-parse --show-toplevel)/plugins/dotnet-test/skills/coverage-analysis"

# Find all Cobertura files and compute top 10 risk hotspots

& "$skillDir/scripts/Compute-CrapScores.ps1" `
    -CoberturaPath (Get-ChildItem -Path . -Filter "*.cobertura.xml" -Recurse).FullName `
    -TopN 10

```

### Extract Methods Below Coverage Thresholds

```powershell

# Filter for methods with line coverage below 80% or branch below 70%

& "$skillDir/scripts/Extract-MethodCoverage.ps1" `
    -CoberturaPath (Get-ChildItem -Path . -Filter "*.cobertura.xml" -Recurse).FullName `
    -Filter below-threshold |
    ConvertFrom-Json |
    Format-Table File, Class, Method, LineCoverage, BranchCoverage, Complexity

```

### Add a Coverage Provider Manually

If auto-detection fails, manually add the collector:

```powershell
dotnet add ./tests/MyApp.Tests.csproj package coverlet.collector --no-restore
dotnet restore ./tests/MyApp.Tests.csproj

```

## Summary

- The **coverage-analysis** skill in `dotnet/skills` provides automated detection, execution, and analysis of .NET test coverage.
- It enriches raw **Cobertura XML** data with **CRAP scores** to identify complex, untested methods that pose the highest change risk.
- Key scripts include `Compute-CrapScores.ps1` for risk calculation and `Extract-MethodCoverage.ps1` for method-level gap analysis.
- All artifacts are generated under `TestResults/coverage-analysis/` without modifying source code.
- The workflow supports both Coverlet and Microsoft.Testing.Extensions.CodeCoverage providers.

## Frequently Asked Questions

### What is a CRAP score and why does it matter?

A **CRAP score** (Change Risk Anti-Pattern) is a composite metric calculated as `complexity² × (1 - coverage)³ + complexity`. It matters because it identifies methods that are both complex and poorly covered—these are the most dangerous to modify without breaking functionality, and therefore the highest priority for new unit tests.

### Can I use this skill with Microsoft’s code coverage tools instead of Coverlet?

Yes. The skill automatically detects whether `Microsoft.Testing.Extensions.CodeCoverage` is already referenced and will prefer it or add it if no coverage provider exists. It supports both **Coverlet** (via `coverlet.collector`) and the Microsoft testing extensions, adjusting the `dotnet test` arguments accordingly (see [`SKILL.md`](https://github.com/dotnet/skills/blob/main/SKILL.md) lines 124-164).

### How do I view the generated coverage report?

The skill writes a markdown summary to [`TestResults/coverage-analysis/coverage-analysis.md`](https://github.com/dotnet/skills/blob/main/TestResults/coverage-analysis/coverage-analysis.md). You can read this directly in any markdown viewer, or use the optional ReportGenerator integration to create HTML reports. For CI/CD pipelines, the Cobertura XML files are compatible with standard coverage visualization tools.

### Does the skill modify my source code or project files?

No. According to [`references/guidelines.md`](https://github.com/dotnet/skills/blob/main/references/guidelines.md) (lines 3-9), the skill may only add coverage provider packages (like `coverlet.collector`) to test projects. It never modifies source code, production project files, or adds test code. All analysis is performed on existing artifacts and output to isolated directories.