Unexpected mutation survived

663 views
Skip to first unread message

Bavo Vander Henst

unread,
Mar 11, 2016, 6:30:23 AM3/11/16
to PIT Users
Hello,

We are currently testing the use of pit for a new project in our company. We started implementing some basic classes to test the power and usefulness of mutation testing.
While testing we noticed something strange and we are unable to tell wether this is expected behaviour or not.

We have the class Request:

public class Request

{

       private HashMap<String, String> fMetaData = new HashMap<String, String>();

        private final String fSomething;


       /**

        * Constructor.

        */

       public Request(final String something)

       {

               assert something != null : "something may not be null";


                fSomething = something;

       }


       /**

        * Constructor.

        */

       public Request(final String something, final Map<String, String> metaData)

       {...}


If we run our test suite we get a perfect score expect for this line:

private HashMap<String, String> fMetaData = new HashMap<String, String>();


Where pit has applied 4 mutations:




1. removed call to java/util/HashMap::<init> → SURVIVED

2. Removed assignment to member variable fMetaData → SURVIVED

3. removed call to java/util/HashMap::<init> → KILLED

4. Removed assignment to member variable fMetaData → KILLED


If we change our code to:

public class Request

{

       private HashMap<String, String> fMetaData;

       private final String fSomething;


        /**

        * Constructor.

        */

       public Request(final String something)

       {

               assert something != null : "something may not be null";


               fMetaData = new HashMap<String, String>();

               fSomething = something;

       }


So we do the assignment in the constructor. Now we get our perfect score:


1. removed call to java/util/HashMap::<init> → KILLED

2. Removed assignment to member variable fMetaData → KILLED



So we have 2 questions. 

1. Why is there a difference between the two approaches, to us they do exactly the same?

2. Why there are seemingly the same mutation for one line that produce different outcomes?


We would really like to understand this more.


Bavo Vander Henst

Product Developer 

henry

unread,
Mar 25, 2016, 6:39:40 AM3/25/16
to PIT Users
Hi Bavo,

Sorry for the slow reply to this - been a touch busy.

It's a little difficult to say what is happening here as I only have a partial picture of your code and cannot see what the tests that exercise it are doing.

My best guess however is as follows.

The first version of your class provides two constructors. The one that takes the map overwrites the value assigned to fMetaData, so the earlier inline call to new HashMap is redundant.

The call is required if the class in constructed using the single argument version.

I'm therefore guessing that all the tests you have that require fMetaData to be non null construct the class using the 2 parameter constructor.

The reason you see two assignments and initialisations of the HashMap is because of the way the java compiler works. Inline field initialisations code is duplicated by the compiler at the start of each constructor.

Personally I recommend only ever having  a single worker constructor which others delegate too. I find this results in clearer code and fewer mistakes.

e.g

public class Request {

 
private final Map<String, String> fMetaData;
 
private final String fSomething;
 
 
public Request(final String something) {
   
this(something, new HashMap<String,String>());

 
}

 
public Request(final String something, final Map<String, String> metaData) {

   
assert something != null : "something may not be null";

   
this.fSomething = something;
   
this.fMetaData = metaData;
 
}

}

See https://ncrcoe.gitbooks.io/java-for-small-teams/content/style/800_provide_no_more_than_one_worker_constructor.html


Henry

Bavo Vander Henst

unread,
Mar 30, 2016, 5:51:54 AM3/30/16
to PIT Users
Hi Henry,

Thank you very much for the detailed answer. Everything is clear now for us.
We were very confused by the fact that pit tried the same mutation twice for one line. Now we understand that there are actually two instances of the same line, so this makes sense. 

You were indeed right that we have used a weird construction that would lead to problems anyway.
We will change this construction in order to not have this problems anymore.

Thanks for giving a deeper understanding of the compiler and making us more confident with pit.
cheers,

Bavo
Reply all
Reply to author
Forward
0 new messages