This document provides an overview of mutation testing and the Pitest tool. It defines mutation testing as seeding artificial defects into source code to check if tests detect them. It describes various mutators that Pitest uses to alter code, such as changing relational operators or removing method calls. The document also outlines how to set up Pitest in a Maven project and review the test coverage report. It concludes that mutation testing can find bugs and redundant code but requires significant time to run all mutants. Not all mutants need to be killed to ensure quality.
Introduction to mutation testing with Pitest, covering agenda: definition, mutators, setup, running tests, reporting, conclusions, and references.
Definition of mutation testing, its historical context, and purpose of detecting the efficacy of tests by introducing mutations.
Overview of mutators run by Pitest, including examples like INCREMENTS_MUTATOR and CONDITIONALS_BOUNDARY_MUTATOR, explaining their functions with examples.
Guidelines on setting up Pitest in Maven and instructions on running mutation tests in a straightforward method.
Insights into the Pitest coverage report, conclusions on the effectiveness of mutation testing, implications of mutation scores, and references for further reading.
Final thoughts on mutation testing, its challenges, and a comprehensive list of references for deeper insights.
Definition
Mutation testing wasoriginally proposed by Richard Lipton as a
student in 1971 and first developed and published by DeMillo,
Lipton and Sayward. The first implementation of a mutation
testing tool has been created by Timothy Budd as part of his PhD
work (titled Mutation Analysis) in 1980 from Yale University. [6]
5.
Definition
Mutation testing isused to design new software tests and
evaluate the quality of existing software tests. Basically the idea
is about seeding artificial defects (mutations) into a source code
and checks whether your test suite finds them.
If you change the source code
▪ your test fails then mutation is killed - It’s good :)
▪ your test passes then mutation survived - It’s bad :(
Negate Conditionals Mutator
if(a == b) {
//some code
}
mutated into
if (a != b) {
//some code
}
Original Mutated
== !=
!= ==
<= >
>= <
< >=
> <=
11.
Void method callmutator
public void myMethod(int a) {
//some code
}
public int rootMethod(int x) {
myMethod(x);
return x;
}
mutated into
public void myMethod(int a) {
//some code
}
public int rootMethod(int x) {
return x;
}
12.
Mutators
Other mutators disabledby default :
▪ CONSTRUCTOR_CALLS - replaces constructor call with null
▪ INLINE_CONSTS - assigns value to non-final variable
▪ NON_VOID_METHOD_CALLS - removes calls to non void methods
▪ REMOVE_CONDITIONALS - removes all conditionals statements
▪ EXPERIMENTAL_MEMBER_VARIABLE - removes assignments to
member variables
▪ EXPERIMENTAL_SWITCH - changes switch statement by replacing
the default label (wherever it is used) with first label. All the other
labels are replaced by the default one
Conclusions
Mutation testing seemspowerful and research indicates that
mutation score is a better predictor of real fault detection rate
than code coverage [2]. However, it has not yet received
widespread popularity.
Creating mutations and executing tests against those mutations
is not a lightweight process and can take quite a lot of time.
In addition to finding bugs, mutation testing is a great way to
find redundant code thus making your code cleaner!
22.
Conclusions
Should all mutantsbe killed ? No, but it depends.
try {
connection.close();
} catch(SQLException e){
doNext("Call DevOps & create JIRA task for Dev :)");
}
To get 100% line coverage here would mean adding a test scenario that throws an
error on connection.close() and verify the call to doNext(..). If you haven’t got it
covered, PIT will remove the call to ‘doNext’ and complain in turn that you didn’t
kill that mutant. By all means do, if your time and budget are limitless.
Remember! Mutant is only an instance in which an artificial change to your code
was not detected by your tests.