CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/stryker-net-mutation

Configures Stryker.NET for mutation testing of .NET Core / .NET Framework projects - installs `dotnet-stryker` global tool, scopes mutation to specific csproj, supports xUnit / NUnit / MSTest, authors `stryker-config.json` with thresholds, runs in CI. Use when a .NET test suite needs mutation-quality verification - closes the .NET ecosystem gap left by Stryker.NET being newer than the JS variant.

80

Quality

100%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

SKILL.md

name:
stryker-net-mutation
description:
Configures Stryker.NET for mutation testing of .NET Core / .NET Framework projects - installs `dotnet-stryker` global tool, scopes mutation to specific csproj, supports xUnit / NUnit / MSTest, authors `stryker-config.json` with thresholds, runs in CI. Use when a .NET test suite needs mutation-quality verification - closes the .NET ecosystem gap left by Stryker.NET being newer than the JS variant.

stryker-net-mutation

Overview

Stryker.NET brings mutation testing to .NET Core and .NET Framework projects, distributed as the dotnet-stryker global tool (introduction).

When to use

  • A .NET test suite (xUnit / NUnit / MSTest) needs mutation-quality verification.
  • A C# library needs higher confidence than coverage alone shows.
  • The team wants Stryker-family parity across JS + .NET.

Step 1 - Install

dotnet tool install -g dotnet-stryker

Per-project (recommended for CI determinism):

dotnet new tool-manifest
dotnet tool install dotnet-stryker

Step 2 - Run

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

The first run discovers the test framework, reports baseline coverage, then mutates and re-tests.

Step 3 - Configure via stryker-config.json

Per stryker-net-config, every option nests under a single stryker-config root object; Stryker.NET publishes no JSON schema, so there is no $schema key to reference.

{
  "stryker-config": {
    "project": "../MyApp/MyApp.csproj",
    "test-projects": ["MyApp.Tests.csproj"],
    "mutation-level": "Standard",
    "thresholds": { "high": 80, "low": 60, "break": 50 },
    "concurrency": 4,
    "reporters": ["progress", "html", "cleartext"]
  }
}

mutation-level controls how aggressive mutations are:

  • Basic (fewest mutators)
  • Standard (default, recommended)
  • Advanced
  • Complete (most mutators; slowest)

Step 4 - Solution mode

For multi-project solutions:

dotnet stryker --solution path/to/MyApp.sln

Stryker.NET discovers all test projects and mutates the production projects each test references.

Step 5 - CI integration

- uses: actions/setup-dotnet@v4
  with: { dotnet-version: '8.x' }
- run: dotnet tool restore
- run: dotnet stryker --break-at 50
- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: stryker-net-report
    path: StrykerOutput/

Reports land under StrykerOutput/<timestamp>/reports/mutation-report.html.

Step 6 - Mutators (per Standard level)

Standard-level mutators cover arithmetic and logical operators, equality, conditional boundary and negation, return-value, statement removal, and string literal. A surviving mutant means the test suite doesn't distinguish the original behavior from the mutated one. Example - conditional boundary:

MutatorExample
Conditional boundary<<=, >>=

The full mutator table and the stryker-config.json option list are in references/stryker-net-mutators.md.

Worked example

MyApp ships a PricingService; MyApp.Tests uses xUnit and is green.

  1. dotnet tool install -g dotnet-stryker, then from MyApp.Tests run dotnet stryker once to confirm xUnit discovery.
  2. Add stryker-config.json pointing project at ../MyApp/MyApp.csproj, mutation-level Standard, thresholds { "high": 80, "low": 60, "break": 50 }.
  3. dotnet stryker writes StrykerOutput/<timestamp>/reports/mutation-report.html, which flags a surviving conditional-boundary mutant: qty > 0 mutated to qty >= 0.
  4. Add an xUnit test at qty == 0; re-running dotnet stryker kills the mutant and lifts the score above the break threshold.
  5. In CI, run dotnet stryker --break-at 50 and upload StrykerOutput/.

Anti-patterns

Anti-patternWhy it failsFix
Running on the entire solution every PRLong runtime; team disables.Scope to changed projects via --project.
mutation-level: Complete from day oneSlowest mode; many irrelevant mutants.Start Standard; promote to Complete for critical libraries.
Ignoring "no coverage" mutants in the reportUntested code; mutation testing can't measure it.Add tests OR exclude those files via --mutate.
Skipping --break-at in CIMutation score regressions slip through.Set --break-at to baseline + small headroom (Step 5).
Running against a Debug buildSlower; some mutators behave differently.Run against Release build for CI gates.

Limitations

  • Newer than StrykerJS. Per stryker-net-intro, the .NET variant filled "what developers perceived as a gap"; feature parity with JS is improving but not 1:1.
  • Async + concurrency mutation can be tricky. Some mutants produce equivalent code; flag + exclude.
  • CI runtime. A medium codebase mutation run takes 10-60 min; not for every PR.

References

  • sni - Stryker.NET overview, NuGet distribution, .NET Core + .NET Framework support, history.
  • snc - stryker-config.json / .yaml file format and the full option list.
  • stryker-mutation - JS sibling with the same Stryker model.
  • pitest-mutation, mutmut-mutation, mull-mutation - per-language alternatives.

SKILL.md

tile.json