CoverBouncer.MSBuild 1.0.0-preview.10

This is a prerelease version of CoverBouncer.MSBuild.
dotnet add package CoverBouncer.MSBuild --version 1.0.0-preview.10
                    
NuGet\Install-Package CoverBouncer.MSBuild -Version 1.0.0-preview.10
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CoverBouncer.MSBuild" Version="1.0.0-preview.10">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CoverBouncer.MSBuild" Version="1.0.0-preview.10" />
                    
Directory.Packages.props
<PackageReference Include="CoverBouncer.MSBuild">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CoverBouncer.MSBuild --version 1.0.0-preview.10
                    
#r "nuget: CoverBouncer.MSBuild, 1.0.0-preview.10"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CoverBouncer.MSBuild@1.0.0-preview.10
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CoverBouncer.MSBuild&version=1.0.0-preview.10&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=CoverBouncer.MSBuild&version=1.0.0-preview.10&prerelease
                    
Install as a Cake Tool

đŸšĒ Cover-Bouncer

The Coverage Doorman for .NET Projects

Cover-Bouncer enforces profile-based code coverage policies in your .NET projects. Tag your files, set thresholds, and let the bouncer keep your coverage standards high.

Why Cover-Bouncer?

Standard coverage tools give you one number: "X% coverage." But not all code is equal:

  • Your critical business logic should have 90%+ coverage
  • Your DTOs might not need any tests
  • Your integration adapters need moderate coverage

Cover-Bouncer lets you enforce different coverage requirements for different parts of your codebase.

🎨 Profiles Are Completely Customizable!

You are NOT limited to any specific profile names or percentages:

❌ Common Misconception: "I must use 'Critical' (90%), 'Standard' (80%), etc."

✅ Reality: "I can use ANY names with ANY thresholds!"

Examples of Valid Profiles:

{
  "profiles": {
    "MustHaveTests": { "minLine": 0.45 },      // Start achievable!
    "NiceToHave": { "minLine": 0.25 },
    "SecurityStuff": { "minLine": 0.75 },      // Your own names!
    "LegacyCode": { "minLine": 0.10 },         // Be honest about reality
    "WillFixLater": { "minLine": 0.0 }
  }
}

Start Where YOU Are:

  • Have 30% coverage now? Start with 35% threshold
  • Have 60% coverage? Maybe aim for 65-70%
  • Have 5% coverage? Start at 10% and celebrate progress!

The goal is IMPROVEMENT, not perfection! 📈

The built-in templates (Basic, Strict, Relaxed) are just suggestions to get you started. Feel free to customize them to match your team's current reality and goals.

Don't try to tag everything at once! CoverBouncer defaults to no coverage enforcement so you can adopt gradually.

Step 1: Install and Initialize

dotnet add MyApp.Tests package CoverBouncer.MSBuild
dotnet coverbouncer init

Your build will pass immediately - untagged files default to NoCoverage (0%).

Step 2: Tag ONE Critical File

Pick your most important file (payments, auth, etc.) and tag it:

// [CoverageProfile("Critical")]
public class PaymentProcessor { }

Step 3: Customize Profiles (Optional)

Adjust thresholds to match your codebase:

{
  "defaultProfile": "NoCoverage",
  "profiles": {
    "Critical": { "minLine": 0.90 },      // Payments, Auth, Security
    "Standard": { "minLine": 0.60 },      // Code you want tested
    "NoCoverage": { "minLine": 0.0 }      // Default - no enforcement
  }
}

Step 4: Expand Gradually

Once Critical is stable, expand to more areas:

# Week 2: Tag your core business logic
dotnet coverbouncer tag --path "./Core" --profile Standard

# Week 3: Tag standard application code  
dotnet coverbouncer tag --pattern "**/*Service.cs" --profile Standard

# Week 4: Tag DTOs to exclude from coverage requirements
dotnet coverbouncer tag --pattern "**/*Dto.cs" --profile Dto

Pro tip: Use --dry-run to preview changes before applying them!

Quick Start

1. Install

# Install CoverBouncer
dotnet add MyApp.Tests package CoverBouncer.MSBuild

# Install Coverlet for coverage collection
dotnet add MyApp.Tests package coverlet.msbuild

2. Initialize

dotnet coverbouncer init

3. Configure Coverage (one-time)

Add to Directory.Build.props:

<PropertyGroup>
  <CollectCoverage>true</CollectCoverage>
  <CoverletOutput>$(MSBuildProjectDirectory)/TestResults/coverage.json</CoverletOutput>
  <CoverletOutputFormat>json</CoverletOutputFormat>
  <EnableCoverBouncer>true</EnableCoverBouncer>
</PropertyGroup>

4. Tag Your Files (Optional!)

Note: Untagged files automatically use your defaultProfile. You can start with zero tags and add them later for files needing different thresholds.

You can tag files manually or use the CLI tagging features:

Manual Tagging

Add a comment with the profile attribute:

// [CoverageProfile("Critical")]
namespace MyApp.PaymentProcessing
{
    public class PaymentService { }
}
Automated Tagging

Interactive Mode (Recommended for beginners):

dotnet coverbouncer tag --interactive

Batch Mode - Tag by pattern:

# Tag all service files with "Standard" profile
dotnet coverbouncer tag --pattern "**/*Service.cs" --profile Standard

# Tag entire folder with "Critical" profile
dotnet coverbouncer tag --path "./Security" --profile Critical

# Tag files from a list
dotnet coverbouncer tag --files services.txt --profile Standard

Smart Detection - Auto-suggest profiles:

# Automatically suggest profiles based on file patterns
dotnet coverbouncer tag --auto-suggest

# Preview changes without modifying files
dotnet coverbouncer tag --pattern "**/*.cs" --profile Standard --dry-run

The CLI automatically detects patterns like:

  • *Controller.cs → Integration
  • *Service.cs → BusinessLogic
  • Security/* → Critical
  • Models/* → Dto

5. Run Tests

dotnet test

Coverage policy automatically enforced! ✅

Features

  • đŸŽ¯ Profile-Based Coverage - Different thresholds for different code types
  • 🤖 Smart Tagging - Interactive, batch, and auto-suggest modes for tagging files
  • 🔌 Drop-In Integration - Works with your existing dotnet test workflow
  • 🔍 Filtered Run Support - dotnet test --filter works without false failures
  • đŸšĢ CI/CD Ready - Blocks merges when coverage drops below thresholds
  • đŸ“Ļ NuGet Packaged - Easy to install and distribute
  • đŸˇī¸ File-Level Tags - Simple attribute-based profile assignment
  • âš™ī¸ Single Config File - No configuration sprawl
  • 🔧 Auto-Configuration - Automatically excludes CoverBouncer from coverage
  • 🎨 Fully Customizable - Use any profile names and thresholds you want

🔍 Filtered Test Runs (--filter)

Running dotnet test --filter "Category=Unit" or any --filter expression? CoverBouncer handles it automatically.

The Problem

When you use --filter, Coverlet still instruments the entire assembly. Files not targeted by the filtered tests appear with 0% coverage, even though no tests were supposed to run against them. Without awareness, CoverBouncer would report false failures for every untargeted file.

How CoverBouncer Solves It

CoverBouncer automatically detects filtered test runs via the $(VSTestTestCaseFilter) MSBuild property. When a filter is active, files with zero covered lines are skipped — they were instrumented but not targeted.

No configuration needed — it just works.

Example: Same Coverage Data, Different Modes

Filtered run (dotnet test --filter "Category=OrderTests"):

â„šī¸  Filtered test run mode: files with zero coverage will be skipped
Coverage Summary by Profile
─────────────────────────────────────────
  ✅ BusinessLogic: 1 passed, 0 failed (80% required)
  ✅ Critical: 1 passed, 0 failed (100% required)
  â­ī¸  4 file(s) skipped (no coverage data in filtered test run)
─────────────────────────────────────────
✅ All 2 files passed coverage requirements

Full run (dotnet test, same data):

Coverage Summary by Profile
─────────────────────────────────────────
  ❌ BusinessLogic: 1 passed, 1 failed (80% required)
  ❌ Critical: 1 passed, 1 failed (100% required)
  ✅ Dto: 1 passed, 0 failed (exempt)
  ❌ Standard (default): 0 passed, 1 failed (60% required)
─────────────────────────────────────────
❌ 3 coverage violation(s) found

âš ī¸ Best Practice: Use Full Runs as Your CI Gate

Filtered runs are great for fast local feedback — validate just the files you're working on. But only a full run validates coverage across your entire codebase.

Recommended CI setup:

jobs:
  quick-check:
    # Fast feedback on PRs — only validates targeted files
    run: dotnet test --filter "Category=Unit"

  coverage-gate:
    # Final gate — validates ALL coverage thresholds
    run: dotnet test

How It Works Under the Hood

Situation CoveredLines Filtered Run Full Run
File targeted by tests, meets threshold > 0 ✅ Pass ✅ Pass
File targeted by tests, below threshold > 0 ❌ Fail ❌ Fail
File NOT targeted (not in filter) 0 â­ī¸ Skip ❌ Fail
File with Dto profile (0% allowed) 0 â­ī¸ Skip ✅ Pass

Key insight: On a filtered run, there's no way to distinguish "file not in the filter" from "file genuinely has no tests." That's why the full run is essential as your final gate — it catches files that truly need tests.

CLI Usage

For CLI usage (outside MSBuild), pass the --filtered flag:

coverbouncer verify --coverage coverage.json --config coverbouncer.json --filtered

Coverlet Integration

CoverBouncer works seamlessly with Coverlet, the popular .NET code coverage library.

Automatic Exclusions

When you add CoverBouncer.MSBuild as a NuGet package, it automatically excludes all CoverBouncer assemblies from Coverlet instrumentation via buildTransitive targets. This prevents warnings about missing debug symbols and improves performance.

What's excluded automatically:

  • [CoverBouncer.Core]*
  • [CoverBouncer.Coverlet]*
  • [CoverBouncer.MSBuild]*

No manual configuration needed! 🎉

Optional: Exclude Test Frameworks

During dotnet coverbouncer init, you'll be prompted to exclude common test frameworks and mocking libraries for better performance. This is optional but recommended:


<PropertyGroup>
  <Exclude>$(Exclude);[xunit.*]*;[FluentAssertions]*;[Moq]*;[NSubstitute]*</Exclude>
</PropertyGroup>

Why Exclude These?

  • Performance: Faster test runs by skipping unnecessary instrumentation
  • Noise Reduction: Focus coverage reports on your actual code
  • No Warnings: Prevents "missing symbols" warnings for NuGet packages

Manual Configuration

If you need more control over Coverlet exclusions:

<PropertyGroup>
  
  <Include>[MyApp]*</Include>
  
  
  <Exclude>$(Exclude);[MyApp.Tests]*;[ThirdParty.*]*</Exclude>
</PropertyGroup>

See Coverlet documentation for more options.

How It Works

  1. Tag files with coverage profiles using file-level attributes
  2. Set thresholds in coverbouncer.json
  3. Run tests - Cover-Bouncer validates coverage automatically
  4. Build fails if any file violates its profile's threshold

Project Structure

  • CoverBouncer.Core - Core coverage policy engine
  • CoverBouncer.Coverlet - Coverlet adapter for coverage data
  • CoverBouncer.MSBuild - MSBuild integration (main user package)
  • CoverBouncer.Analyzers - Roslyn analyzers (coming soon)

Example Configuration

{
  "coverageReportPath": "TestResults/coverage.json",
  "defaultProfile": "Standard",
  "profiles": {
    "Critical": {
      "minLine": 0.90
    },
    "Standard": {
      "minLine": 0.60
    },
    "Integration": {
      "minLine": 0.40
    },
    "Dto": {
      "minLine": 0.00
    }
  }
}

Note: Values are decimals (0.0-1.0), not percentages. So 0.90 = 90% line coverage.

Installation & Usage

Automatically validates coverage after dotnet test:

dotnet add package CoverBouncer.MSBuild

Add to your test project's .csproj:

<PropertyGroup>
  <EnableCoverBouncer>true</EnableCoverBouncer>
  
  
  
</PropertyGroup>

See MSBuild Configuration for all available properties.

CLI Tool

For manual validation or CI/CD scripts:

dotnet tool install -g CoverBouncer.CLI
coverbouncer verify --coverage coverage.json --config coverbouncer.json

Validation Tests

This project includes comprehensive validation tests:

  • ✅ All files pass thresholds
  • ✅ Mixed pass/fail scenarios
  • ✅ Critical violations
  • ✅ Untagged file handling
  • ✅ Multiple profile scenarios
  • ✅ Edge cases (exact thresholds, zero coverage)
  • ✅ Real-world project simulation
  • ✅ Filtered test run (untargeted files skipped)
  • ✅ Full run with same data (contrast — same data fails without filter awareness)

Run validation tests: dotnet test tests/CoverBouncer.ValidationTests

Building from Source

# Build all projects
./build.sh build

# Run all tests
./build.sh test

# Create NuGet packages
./build.sh pack

# Install CLI tool locally
./build.sh install-cli

Documentation

Contributing

We welcome contributions! See CONTRIBUTING.md for details.

License

MIT License - see LICENSE for details.


Built with â¤ī¸ for the .NET community

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-preview.10 41 3/19/2026
1.0.0-preview.9 66 3/5/2026
1.0.0-preview.8 46 2/25/2026
1.0.0-preview.7 59 1/1/2026
1.0.0-preview.6 57 12/31/2025
1.0.0-preview.5 60 12/31/2025
1.0.0-preview.4 56 12/31/2025
1.0.0-preview.3 180 12/15/2025
1.0.0-preview.2 121 12/14/2025
1.0.0-preview.1 114 12/13/2025

ANSI colorized output for terminal clarity. Compact violation format (actual/required). Relative file paths in error output. Package icon added.