Continuous Integration with dotCover: Setup and Best PracticesContinuous integration (CI) ensures your codebase stays healthy by running builds and automated tests whenever changes are made. Measuring code coverage in CI helps teams maintain test quality and identify untested paths. dotCover, JetBrains’ .NET code coverage tool, integrates with CI systems to produce reliable coverage reports for .NET Framework, .NET Core, and Mono projects. This article covers end-to-end setup, configuration examples for popular CI providers, best practices, and troubleshooting tips.
What dotCover provides for CI
- Code coverage measurement for unit and integration tests across .NET runtimes.
- Report generation in multiple formats (HTML, XML, JSON) suitable for CI artifacts.
- Coverage thresholds and enforcement to fail builds when coverage drops below set values.
- Integration with test runners (dotnet test, NUnit, xUnit, MSTest) and build systems (MSBuild, Cake, TeamCity, Azure DevOps, GitHub Actions, GitLab CI).
- Filters to include/exclude assemblies, namespaces, or files from coverage.
Prerequisites
- A working .NET solution with unit tests (xUnit, NUnit, MSTest, etc.).
- A CI server (examples below use GitHub Actions, Azure DevOps, GitLab CI, and Jenkins).
- dotCover command-line tools (dotCover CLI) — part of JetBrains dotCover distribution. For CI you can either:
- Install dotCover on the build agent, or
- Use JetBrains-provided containers/images that include dotCover, or
- Download and unpack dotCover as part of the pipeline steps.
dotCover CLI basics
dotCover CLI provides commands to run tests under coverage, merge snapshots, and create reports. Key commands:
- cover — run tests under coverage and produce a coverage snapshot.
- merge — merge multiple coverage snapshots into one.
- report — convert a snapshot to HTML, XML, or JSON.
Typical workflow:
- Use dotCover cover to execute your test runner and create a .dcvr snapshot.
- Optionally run multiple cover steps (for parallel matrix jobs) and merge snapshots.
- Run dotCover report to export human-readable reports and CI artifacts.
- Optionally evaluate coverage statistics and fail the build if thresholds aren’t met.
Example dotCover cover usage (CLI):
dotCover.exe cover --TargetExecutable="dotnet" --TargetArguments="test MySolution.sln --no-build" --Output="coverage.dcvr"
Generate an HTML report from the snapshot:
dotCover.exe report --Source="coverage.dcvr" --Output="coverage.html" --ReportType=HTML
Generate XML (for CI tools / coverage badges):
dotCover.exe report --Source="coverage.dcvr" --Output="coverage.xml" --ReportType=NCOVER
Note: ReportType values include HTML, XML formats like NCOVER and DOTCOVER, and JSON in newer versions.
Example: GitHub Actions
- Create workflow file: .github/workflows/ci.yml
- Key steps: restore, build, test-with-dotcover, publish artifacts.
Example workflow:
name: CI on: [push, pull_request] jobs: build-and-test: runs-on: windows-latest steps: - uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x - name: Install dotCover run: | curl -L -o dotcover.zip "https://download.jetbrains.com/resharper/dotCover.2024.3.zip" unzip dotcover.zip -d dotcover - name: Restore run: dotnet restore - name: Build run: dotnet build --no-restore -c Release - name: Run tests under dotCover run: | ./dotcover/tools/dotCover.exe cover --TargetExecutable="dotnet" --TargetArguments="test --no-build --logger:trx" --Output="coverage.dcvr" - name: Create HTML report run: ./dotcover/tools/dotCover.exe report --Source="coverage.dcvr" --Output="coverage.html" --ReportType=HTML - name: Upload artifact uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage.html
Tips:
- Use windows-latest when you need the Windows build agent; dotCover also works on Linux via Mono or .NET Core depending on version.
- For matrix builds (multiple frameworks/OS), generate snapshots per job and merge later.
Example: Azure DevOps
- Use a pipeline YAML with a step to download and extract dotCover, run dotCover cover, then report.
Key tasks:
- Use the Command Line task to run dotCover commands.
- Publish the report as a pipeline artifact or publish code coverage via Azure DevOps Coverage Publisher (you may need to transform dotCover XML to a supported format).
Example: GitLab CI
- Use a job that installs dotCover (download/unpack), runs tests with dotCover, and saves coverage artifacts.
- For Linux runners, ensure dotCover version supports .NET Core and Linux; otherwise use a Windows runner or Docker image with dotCover preinstalled.
.gitlab-ci.yml snippet:
stages: - test unit_tests: image: mcr.microsoft.com/dotnet/sdk:8.0 stage: test script: - curl -L -o dotcover.zip "https://download.jetbrains.com/resharper/dotCover.2024.3.zip" - unzip dotcover.zip -d dotcover - dotnet restore - dotnet build -c Release - ./dotcover/tools/dotCover.exe cover --TargetExecutable="dotnet" --TargetArguments="test --no-build" --Output="coverage.dcvr" - ./dotcover/tools/dotCover.exe report --Source="coverage.dcvr" --Output="coverage.xml" --ReportType=NCOVER artifacts: paths: - coverage.xml - coverage.html
Example: Jenkins
- Use a Windows agent or Docker image with dotCover.
- Execute dotCover CLI in a shell/batch step, then archive artifacts.
- Optionally use the Jenkins Cobertura or JaCoCo plugins if you convert dotCover output to supported formats (NCover/Cobertura) or use the HTML report.
Parallel and matrix job strategies
- Run tests in parallel across multiple runners to speed up CI. Each job should produce a separate .dcvr snapshot.
- After all matrix jobs finish, merge snapshots:
dotCover.exe merge /Source="job1.dcvr;job2.dcvr;job3.dcvr" /Output="merged.dcvr"
- Generate a single report from merged.dcvr.
Coverage thresholds & build enforcement
- Use dotCover’s ability to export coverage statistics (total statement coverage, per-assembly, per-class). Parse the XML/JSON to obtain numeric values and fail the build if below thresholds.
- Example (pseudo):
- Generate JSON report: dotCover report –ReportType=JSON.
- Use a small script (PowerShell/Bash) to read coverage percent and compare to required threshold.
- Exit non-zero to fail CI when threshold not met.
PowerShell example to fail when coverage < 80%:
$json = Get-Content coverage.json | ConvertFrom-Json $coverage = $json.Summary.TotalCoveragePercent if ($coverage -lt 80) { Write-Error "Coverage $coverage% < 80%"; exit 1 }
Filters: what to include/exclude
- Exclude generated code, DTOs, third-party libraries, and auto-generated files.
- Include only assemblies and namespaces that contain business logic.
- Use dotCover filters in the cover command or via XML settings. Example filter to exclude a namespace:
<Filters> <Exclude> <ModuleMask>*.Tests</ModuleMask> <NamespaceMask>MyProject.Generated*</NamespaceMask> </Exclude> </Filters>
Pass settings with –AttributeFilters or a settings file depending on dotCover version.
Best practices
- Keep coverage thresholds realistic and focused: set overall and per-critical-assembly thresholds.
- Fail the build only for meaningful drops (e.g., new code coverage < target) rather than transient small fluctuations.
- Prefer tests that are fast and deterministic to keep CI quick.
- Use merged snapshots for matrix builds to get accurate global coverage.
- Exclude generated code and third-party code from coverage metrics.
- Store coverage reports as CI artifacts for manual inspection.
- Automate trend tracking (use a coverage badge or store historical metrics) to detect regressions.
- Review quality, not just quantity—use coverage as a guide, not an absolute guarantee of correctness.
Troubleshooting
- If dotCover fails to instrument assemblies, ensure the test runner and target framework are supported by the dotCover version used.
- On Linux containers, ensure Mono or .NET support is compatible with the dotCover build. Consider Windows runners if issues persist.
- Long-running tests: increase CI job timeouts or split tests.
- If coverage is unexpectedly low, verify filters aren’t excluding valid assemblies and that tests are actually executing the code paths (use verbose test logs).
- For flaky integration with test runners, run the test command locally with dotCover to reproduce and debug.
Example end-to-end checklist
- [ ] Add dotCover download/extract step to pipeline.
- [ ] Run dotCover cover around your test command.
- [ ] Generate report(s) in HTML and machine-readable format.
- [ ] Archive reports as CI artifacts.
- [ ] Parse coverage and enforce thresholds if needed.
- [ ] Exclude generated/third-party code via filters.
- [ ] Merge per-job snapshots for matrix runs.
- [ ] Track coverage trends and add badges if desired.
Using dotCover in CI helps keep tests honest and track untested code growth over time. With correct configuration, filtered measurements, and sensible thresholds, dotCover integrates cleanly into modern CI pipelines and provides actionable coverage insight.
Leave a Reply