DEV Community

Cover image for Mutation Testing in .NET 10
Christian Alt-Wibbing
Christian Alt-Wibbing

Posted on

Mutation Testing in .NET 10

Why your tests might be lying to you and how to catch them

I've seen projects with 90% code coverage that still burned in production. I've seen tests that stayed green no matter what someone changed in the code. And I've seen teams feel completely safe — even though their tests were basically checking nothing.

Why the thoughtworks technology radar puts this on the table

In the current Technology Radar (Volume 34, April 2026), Mutation Testing sits in the Trial ring. That means: it's worth trying. It's no longer experimental, but it's also not yet standard everywhere.

And the Radar gives a very concrete reason why this topic matters so much right now: AI is writing more and more code and more and more tests. Tools like GitHub Copilot generate tests automatically. That sounds convenient.

But the Radar warns about a dangerous phenomenon: so-called "perpetually green tests". that are tests that always stay green, no matter what you change in the code. Mutation Testing is the answer to that.

The Radar also highlights something broader: as AI-generated code becomes the norm, the risk of "codebase cognitive debt" grows. Code gets written faster than it gets understood. Mutation Testing acts as a reinforcement layer it forces the question: does this test actually verify anything?

What is Mutation Testing?

Imagine someone sneaks into your code at night and changes tiny things.

> becomes >=
+ becomes -
true becomes false

These small sabotages are called mutants.

The next morning, your test suite runs. And now comes the critical question: do your tests notice that someone cheated?

If yes than the mutant is killed. Your tests did their job.
If no than the mutant survived. And that's a real problem.

That's Mutation and the question is:
do my tests actually verify what's happening in the code?

The difference to Code Coverage

Code coverage is a number that tells you:

"This line was executed during testing."

Nothing more. It doesn't tell you whether your test actually checks what's happening there.

Here's a concrete example.

public bool IsAdult(int age) => age >= 18;
Enter fullscreen mode Exit fullscreen mode

Your test calls IsAdult(20) and expects true. Coverage: 100%. Everything green.

But what happens if someone changes >= to >? Your test doesn't notice it because 20 returns true in both cases. The mutant survives.

Mutation Testing would have caught that immediately.

"Code coverage tells you which lines were executed. Mutation testing tells you whether your tests actually verify anything. That is a fundamental difference."

Stryker.NET

In the .NET world, Stryker.NET is the tool of choice. And the best part: it's completely free. Open source, MIT license, available on GitHub. No invoice coming.

Installation

dotnet tool install -g dotnet-stryker
Enter fullscreen mode Exit fullscreen mode

Running

cd %(TestDirectory)%
dotnet stryker
Enter fullscreen mode Exit fullscreen mode

That's it. No configuration needed the first time. Stryker finds the matching project on its own.

The HTML Report opens automatically in your browser. You immediately see:

  • Mutation Score: e.g. 73%
  • Killed: how many mutants your tests caught
  • Survived: how many mutants slipped through
  • No Coverage: code without any tests

When does Mutation Testing make sense

Mutation Testing is not a cure-all. It's a precise tool for precise places. The golden rule: Mutation Testing follows complexity.

Where it make sense

  • calculations and pricing logic
  • Authorization checks and security rules
  • Status transitions (e.g. Order → Paid → Shipped)
  • Discount rules and deadline calculations
  • Complex validation logic

Where it brings no value

  • Simple CRUD services: Load → Transform → Return
  • Pure database queries without business logic
  • Infrastructure code (logging, configuration)

The Onion Architecture is the ideal prerequisite

If your project is built with Onion Architecture, you have the perfect setup for Mutation Testing. Thats because the Domain/Core is completely isolated.

That means it contains entities with business rules, value objects, domain services, and validation logic. Exactly where a bug really hurts. A bug in the infrastructure? Painful. A bug in the domain logic? Catastrophic.

Pros and Cons

Pros

  • Makes real test quality visible
  • Exposes dead tests or tests that always stay green no matter what
  • Protection against "perpetually green tests"
  • Confidence in critical domain logic during refactoring
  • No major restructuring of existing architecture needed

Cons

  • Slow on large projects because every mutant runs the entire test suite
  • A lot of noise on the first report (in my case hundreds of surviving mutants was overwhelming)
  • Not useful everywhere (CRUD-heavy services)
  • False sense of security possible — high score ≠ good software overall

The real goal is the change in the mindset

For me** the Mutation Score is not the goal. The changed mindset when writing tests is the goal.**

I think after a few months with Mutation Testing the team automatically starts thinking differently_. We will writing the tests automatically that we kill all mutants._

My summary

Mutation Testing is not rocket science. It's an tool that shows you whether your tests are working well. And with Stryker.NET in .NET 10 you can check that within minutes.

Better to know where the gaps are now than when a bug shows up in production.

Top comments (0)