2

I am initializing a final int field in a constructor by inspecting a local scale int value with respect to zero.

My code is of the form:

final int scale = calculateScale();
if (scale == 0) {
    this.scale = 0;
} else if (scale > 0) {
    this.scale = 1;
} else {
    this.scale = -1;
}

I would expect this is a common pattern, since a Comparator also returns one of three values, -1, 0 or 1.

Pitest complains about the scale > 0 conditional, declaring the mutation scale >= 0 survives.

However, scale cannot be zero at that point, otherwise it would have entered the first conditional block.

In my opinion, pitest is testing an invalid mutation, and a quick dataflow analysis would reveal scale cannot be zero.

I don't like to modify code to fool pitest, but how could this be expressed such that pitest doesn't complain?

gary
  • 498
  • 4
  • 9

2 Answers2

0

From What is mutation test?

When the application code changes, it should produce different results and cause the unit tests to fail. If a unit test does not fail in this situation, it may indicate an issue with the test suite.

Your code is definitely correct, and I guess your unit test is also correct. So what's wrong?

IMO, what I learn from your example is that.

Mutation Test also aim for Conciseness

Which means there is no space for us to modify the program without changing the behaviour.

Let say someone replaced > with >= accidentally,

...
} else if (scale >= 0) {
    this.scale = 1;
}...

The unit test will still pass, everything will still work. Except the code become confusing. Reader will wonder why it is >= and not >. Not possible to happen? Who knows!

What are the solutions?

  1. Reorder the condition, this is somehow "tricky". But at least making the code more robust.
if (scale < 0) {
    this.scale = -1;
} else if (scale > 0) {
    this.scale = 1;
} else {
    this.scale = 0;
}
  1. Use Integer.signum(int i), which just return what you need in this case.

Returns the signum function of the specified int value. (The return value is -1 if the specified value is negative; 0 if the specified value is zero; and 1 if the specified value is positive.)

samabcde
  • 6,988
  • 2
  • 25
  • 41
  • 1
    Yes, you are technically correct to reduce the logic to the signum function in the simplified example I gave. In my actual code, the magnitude of the value is still important, it's not the result of a Comparator.compareTo() call. I should have provided a more appropriate example. – gary Aug 23 '22 at 15:24
0

Such a syntactical change to the source code that does not change the semantics is called an equivalent mutant. It cannot be killed by definition.

Equivalent mutants are a common problem in mutation testing. Pitest's mutation "operators are largely designed to be stable (i.e not be too easy to detect) and minimise the number of equivalent mutations that they generate" (http://pitest.org/quickstart/mutators/).

You need to live with those, it does not make sense to change the code just to satisfy Pitest.

nrainer
  • 2,542
  • 2
  • 23
  • 35