Stryker: Excluding Code
Excluding lines of code where mutation testing doesn’t work
Sometimes there is code that, when a mutation is applied, the resulting code will still pass the tests.
Here is an example (taken from the Strkyer documentationfor disabling mutations):
function max(a, b) {
return a < b ? b : a;
}
One mutation that is applied here, the EqualityOperator
mutation,
turns this into:
function max(a, b) {
return a <= b ? b : a;
}
The thing is, that is still a correct solution, so this mutation will not be killed by any correct test.
We can fix this by using a comment to disable mutations. This comment disable all mutations on the next line of code:
function max(a, b) {
// Stryker disable next-line all
return a <= b ? b : a;
}
But a better approach is to just disable the single mutation that is causing the trouble, since other mutations of this line still result in mutations that can and should be killed by tests:
function max(a, b) {
// Stryker disable next-line EqualityOperator
return a <= b ? b : a;
}
It is possible to add an explanation to comments like this by putting a colon at the end, like this:
function max(a, b) {
// Stryker disable next-line EqualityOperator: The <= mutant results in an equivalent mutant
return a <= b ? b : a;
}
For more information, and a more detailed explanation, see:
GET
as default
Here is an example where "GET"
is the default. Replacing it with empty string ""
results in an equivalent mutation. As a result, we exclude the line with a // Stryker disable next-line all
directive in a comment.
Optional Chaining
Sometimes you write a line of code that uses optional chaining to avoid null pointer references as in this line of code:
const isUserInCommon = currentUser?.root?.user?.commons?.some(common => common.id === parseInt(commonsId));
Stryker will mutate this by trying to remove each ?
one at a time. You can see that on the Stryker report (in HTML format) by clicking on each of the red circles in turn to see the mutation.
There are two approaches to addressing the surviving mutations here:
- The hard way: write tests for each of the possibilities that each of the levels in this nested hierarchy might be null.
- If any of those is a real bug that you encounter, or reasonaly expect to encounter, than it might make sense to do that.
- But often, the chaining is just a “defensive programming” move to make the code more robust to failure and you don’t expect this to crop up.
- The easy way: use
// Stryker disable next-line OptionalChaining
right above this line to exclude only the optional chaining from mutation testing.- The risk is that you might not have tests that cover what happens if one or more of these reference does end up being null.
- But sometimes that’s risk is worth taking vs the effort of writing tests for each level in a hierarchy like this one.