Search code examples
javajunit5mutation-testingpitest

How to refactor survived mutations away for try-resource and an equivalent mutant?


I am experimenting with junit5 and pitest. My code under test looks like:

// [...]
InputStream istream = this.getClass().getResourceAsStream("/" + file.getName());
if (istream == null) // 1. negated condition -> suvived 
 {
  istream = Files.newInputStream(this.files.get(varname).toPath(), StandardOpenOption.READ);
 }
try (BufferedReader reader = new BufferedReader(new InputStreamReader(istream, StandardCharsets.UTF_8))) // 2. removed call to java/io/BufferedReader::close → SURVIVED // 3. removed call to java/lang/Throwable::addSuppressed → SURVIVED
 {
  // [...]
 } // 4. removed call to java/io/BufferedReader::close → SURVIVED

Within this small block of code I have left 4 survived mutations I would like to kill. Killing could happen by adding/changing a test or also by refactoring the code.

My problem is now that the first mutation is an equivalent mutant - were I have no idea how to refactore it away. The other three mutations are implicit by the try-resource-statement.

So my question is how to refactor this 4 mutations away? Because I am sure they can not be killed by additional/changed tests.


Solution

  • The first mutant is only equivalent if the observable behavior of the function is the same on both sides of the if statement.

    For this to be the case 'file' and 'this.files.get(varname)' would need to always resolve to the same inputstream.

    If they can resolve to different inputstreams then a test can be constructed to kill the mutant.

    If they will always resolve to the same thing, then why is the first branch needed? Unless it exists for is some concern that cannot be tested (e.g. performance) then the first branch is not required and the stream can always be resolved from 'this.files.get(varname).toPath()'.

    The other mutants are a little trickier.

    They are equivalent as they deal with a non unit testable concern (resource management). More importantly they're 'junk', as they are a compiler construct and do not map directly back to code.

    Pitest tries to filter out junk mutants like these, but the filtering is imperfect as they are not clearly identified as compiler constructs in the byte code.

    The 1.4.8 release correctly filters out all these mutants if I paste your snippet into a file and edit so that it compiles. If you can paste a full compilable class that reproduces the issue I can see if the filtering can be tweaked to pick up these mutants in context.