Mutants in Testing

Mutants in Testing

This happened to me today ! So this post is based on a true story.

Let's imagine the following scenario: you finished after long hours and lots of manual testing your feature and QA also tested it and everything was ok !!. As usual and as you are agile you move to the next task !!.

So you are coding as usual and reading some news in your secondary big monitor. But an instant message popup suddenly appears from a co-worker from another team telling you that your feature is no longer working !!

via GIPHY

So you tell your co-worker: impossible ! It must be on your side. I will check the logs !

But as you read the logs you notice a weird behavior and a NPE(NullPointerException). However you think to yourself. This is not possible !!

And after doing some manual tests all morning. You realize that something has happened. You open your IDE and read the code and notices that a refactor was done but nothing special. All tests are working !! How can be this possible ?. But you remember that feature that there was a default behavior if a parameter was not sent in the request !.

So you check your code again:

SomeBigClass.java

// lines of code
public SomeBigResponse bigMethod(SomeBigRequest request) {
    // lines of code
     int result = newRefactoredClass.process(request.someNonMandatoryValue);
     // lines of code
     return anyResponse;
}

NewRefactoredClass.java

// lines of code
/*
    Super Generic Awesome Method
*/
int process(String value) {
     return searcher.search(value);
}
// lines of code

So where the hell the code for the default behavior go ?!!!

Then you check the history and this is how it was before:

SomeBigClass.java

// lines of code
public SomeBigResponse bigMethod(SomeBigRequest request) {
    // lines of code
     int result = process(request.someNonMandatoryValue);
     // lines of code
     return anyResponse;
}
// lines of code
private int process(String value) {
     int result = searcher.search(value);
     if (result != NO_FOUND) {
        return result;
     }
     return DEFAULT_VALUE;
}

Madness !! And before talking to your teammate whom modified the code you open the test:

SomeBigClassTest.java

@Test
public void testProcess() {
     SomeBigRequest request = new SomeBigRequest();
     request.someNonMandatoryValue = "Test";
     SomeBigResponse response = someBigClass.bigMethod(request);
     Assertions.assertEquals(response.result, "Voila!")
}

@Test
public void testProcessWithExceptions() {
     // some code that check when an exception happens
}

By the gods ! you realize that your test of the class was ok for happy and edge scenarios ! But the default behavior for the process() method was not tested !!

Ehh so what now ?

There are 2 possible reasons why this happened :

  1. The developer(me) did not cover well all the use cases =>

TDD might helped to avoid this problem or just validating the code agains the user story/use cases on time.

  1. The other developer whom refactored the code did not add tests before refactoring the code =>

Golden Rule : > Always add tests before refactoring legacy or any code !

So as this happened to me; it could happened to anyone( i hope so :'( ).

Is there any way to know if your tests are good or are testing a good scope ?

Of course there is ! And that is the main purpose of this post besides just me doing a mea culpa.

And it is called: Mutation Testing !

Here is a good article about it: javatpoint.com/mutation-testing

But summing up: it is when you modify some key code and see how the tests behave.

For example:

image.png

So if your test is green despite of the change as the code shown in the image above. It means your test is as useful as testing 1==1. Here you have the option to improve your test and make it more useful.

In conclusion just don't blame your co-worker for doing dumb refactors but protect your code from them with some mutants in your testing process !!

// :wq