Mutation testing and refactoring: whenever we run mutation testing on a repo that wasn't developed using TDD approach, chances are we'll end up with a lot of surviving mutants. Most teams look into the mutants report and realize that mutants indicate they need to write more tests
What typically happens after they go back to the drawing board and write more tests, the number of surviving mutants drop. So it's a great sign and a victory to celebrate. Their automated tests suite suddenly got better. Still, there inevitably remain some surviving mutants.
At that point, some teams may just shrug and say "god enough". More ambitious teams may attempt to get rid of those pesky surviving mutants. It sometimes becomes a question of professional pride. And it is at that point when things start to become interesting.
Success begets success, and similar to how they eliminated so many mutants by adding more tests, the team now presses on in the attempt to add even more tests & eliminate those surviving mutants. But a curious thing sometimes occurs -- they hit the wall and cannot add more tests!
What this impasse signals is the fact the some part of the shipping code is *untestable*. This is where mutation testing becomes really interesting. It actually helps uncover untestable code. You see, you want to write more tests (good intention) but you realize you can't do it.
I don't know if you've ever experienced similar situation, but I have (many times), and it can be very frustrating. Like, I *want* to write the test that will kill that mutant, but something about my code is not letting me do it. Why is the code pushing back?
The reason the code is pushing back is simple: complexity crept in. The code isn't sufficiently modularized, it is bunched up. Because of that, altering the logic buried in the code should alter the produced values and tests should detect the error, but they can't.
When that happens, a mutant survives. And that mutant is our friend. How so? It is a canary in a mineshaft. It is alerting us to the fact that our code is complex, and it needs to get simplified. The code got bloated, time to deflate it. Once we deflate it, the mutant gets killed
How do we deflate bloated code? One word: refactor. So here finally we see the relation between mutation testing and refactoring. Without mutation testing, many hidden crevices in the code remain infested with bloat, while we dangerously hold that our code is solid & clean.
The moral of the story? Embrace TDD. Embrace code hygiene. If you do that, you won't have to waste time on the song-and-dance with seeing surviving mutants, adding tests to kill them, seeing some survive, rushing to refactor. With TDD, you preempt all that (cheaper).
If I'm doing TDD every step of the way, whenever I stop to do the sanity check (i.e. mutation testing) chances are much smaller that I'll have surviving mutants than if I don't care about proper code hygiene. So TDD is the best remedy for bloated, untestable code.
You can follow @alexbunardzic.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled:

By continuing to use the site, you are consenting to the use of cookies as explained in our Cookie Policy to improve your experience.