Professional Documents
Culture Documents
Mutation Testing: How Good Are My Tests?
Mutation Testing: How Good Are My Tests?
Software Engineering
Gordon Fraser Saarland University
subsumes
Path testing
different coverage criteria.
Boundary Compound
interior testing condition testing
MCDC testing
CoC
Practical Criteria
Branch and
LCSAJ testing
condition testing
All-DU paths
GACC
All uses
CC PC
All-p-some-c
Statement testing
3
This version of the unit test has the
identical coverage as the test on
the previous slide - but it will detect
/**
no faults (except program crashes).
* Make sure Double.NaN is returned iff n = 0
*
*/
public void testNaN() {
StandardDeviation std = new StandardDeviation();
Double.isNaN(std.getResult());
std.increment(1d);
std.getResult();
}
Coverage is not
changed!
6
Learning from Mistakes
Mutants
Mutant
Slightly changed version of original program
Syntactic change
Valid (compilable code)
Simple
Programming glitch
Based on faults
Fault hierarchy
9
Generating Mutants
Mutation operator
Rule to derive mutants from a program
10
while(y != 0) {
tmp = x % y;
x = y;
y = tmp;
}
return x;
}
11
while(y != 0) {
tmp = x % y;
x = abs(y);
y = tmp;
}
return x;
}
12
ABS - Absolute Value
Insertion
int gcd(int x, int y) {
int tmp;
while(y != 0) {
tmp = x % y;
x = y;
y = tmp;
}
return -abs(x);
}
13
while(y != 0) {
tmp = x % y;
x = y;
y = tmp;
}
return 0;
}
14
AOR - Arithmetic
Operator Replacement
int gcd(int x, int y) {
int tmp;
while(y != 0) {
tmp = x % y;
x = y;
y = tmp;
}
return x;
}
15
AOR does not only replace the
AOR - Arithmetic operators, but also the two special
cases where x + y is mutated to x
Operator Replacement and to y, i.e. dropping the other
operand.
while(y != 0) {
tmp = x * y;
x = y;
y = tmp;
}
return x;
+, -, *, /, %, **, x, y
}
16
ROR - Relational
Operator Replacement
int gcd(int x, int y) {
int tmp;
while(y != 0) {
tmp = x % y;
x = y;
y = tmp;
}
return x;
}
17
while(y > 0) {
tmp = x % y;
x = y;
y = tmp;
}
<, >, <=, >=, =,
return x;
}
!=, false, true
18
COR - Conditional
Operator Replacement
if(a && b)
if(a || b)
if(a & b)
if(a | b)
if(a ^ b)
if(false)
if(true)
if(a)
if(b)
19
x = m >> a
x = m >>> a
x = m
20
LOR - Logical
Operator Replacement
x = m & n
x = m | n
x = m ^ n
x = m
x = n
21
ASR - Assignment
Operator Replacement
int gcd(int x, int y) {
int tmp;
while(y != 0) {
tmp = x % y;
x = y;
y = tmp;
}
return x;
}
22
ASR - Assignment
Operator Replacement
int gcd(int x, int y) {
int tmp;
while(y != 0) {
tmp = x % y;
x -= y;
y = tmp;
}
while(y != 0) {
tmp = x % y;
x = y;
y = tmp;
}
return x;
}
24
UOI - Unary Operator
Insertion
int gcd(int x, int y) {
int tmp;
while(y != 0) {
tmp = x % +y;
x = y;
y = tmp;
}
return x; +, -, !, ~,++,--
}
25
UOD - Unary
Operator Deletion
if !(a > -b)
if (a > -b)
if !(a > b)
26
while(y != 0) {
tmp = x % y;
x = y;
y = tmp;
}
return x;
}
27
SVR - Scalar Variable
Replacement
int gcd(int x, int y) {
int tmp;
while(y != 0) {
tmp = y % y; tmp = x % y
x = y; tmp = x % x
y = tmp; tmp = y % y
}
x=x%y
return x;
y=y%x
} tmp = tmp % y
tmp = x % tmp
28
OO Mutation
So far, operators only considered method
bodies
Class level elements can be mutated as
well:
29
OO Mutation
AMC - Access Modifier Change PTC - Parameter Type Change
HVD - Hiding Variable Deletion RTC - Reference Type Change
HVI - Hiding Variable Insertion OMC - Overloading Method
Change
OMD - Overriding Method
Deletion OMD - Overloading Method
Deletion
OMM - Overridden Method Moving
OMR - Overridden Method AOC - Argument Order Change
Rename ANC - Argument Number Change
SKR - Super Keyword Deletion TKD - this Keyword Deletion
PCD - Parent Constructor Deletion SMV - Static Modifier Change
ATC - Actual Type Change VID - Variable Initialization Deletion
DTC - Declared Type Change DCD - Default Constructor 2
30
Interface Mutation
Integration testing
31
Order of Mutants
First order mutant (FOM)
Exactly one mutation
#HOM = 2 #FOM -1
32
Competent
Programmer
Hypothesis
A programmer writes a program that is in
the general neighborhood of the set of
correct programs
33
Coupling Effect
34
Competent Programmer
Coupling Effect
Hypothesis 35
Original Program
------------------- ------------------- ------------------- -------------------
------------------- ------------------- ------------------- -------------------
+
------------------- ------------------- ------------------- -------------------
------------------- ------------------- ------------------- -------------------
------------------- -------------------
|| ------------------- -------------------
------------------- ------------------- -------------------
< -------------------
------------------- ------------------- ------------------- -------------------
36
-------------------
-------------------
-------------------
------------------- Live mutant - we need more tests
-------------------
||
-------------------
-------------------
-------------------
-------------------
-------------------
------------------- Dead mutant - of no further use
-------------------
-------------------
<
-------------------
37
0,*!#@0A.(#-.(4#$%&!'(,#-.(.L!#$%&!'.(#-.(.7
!" #$%&!'()*&!+!(,#-.(./ A!
Input
#$%&!'.)*&!+!.(#-.(./
0,*!-1!+!2/
9$0:(!4'()*&7!" B
#
>%:?( ;&<(
#$%&!#/ C
"
#!+!'()*&/
03!4#!++!5657!"!!
>%:?( ;&<(
$
!(:?(03!4#!++!5K57!" D 8!
'.)*&!+!5!5/ E
%
>%:?( ;&<(
(:?(
Reachability F G
& 0,*!.0@0*A$0@$!+!B(CAD%:<(?E'466()*&7F/ '
'.)*&!+!'()*&/ 0,*!.0@0*A:-9!+!B(CAD%:<(?E'466()*&7F/
8 03!4.0@0*A$0@$!++!GH!II!.0@0*A:-9!++!GH7!"
>%:?( ;&<(
H I)
Propagation (:?(!" ( -1!+!H/
'.)*&!+!HJ!'!.0@0*A$0@$!6!.0@0*A:-9/ 8
8
Infection
66.)*&/
66()*&/
L
*
'.)*&!+!5=25/
&(*<&,!-1/
M+ 8
8
38
Figure 12.2: The control flow graph of function cgi decode from Figure 12.1
-------------------
-------------------
-------------------
-------------------
-------------------
||
-------------------
Mutation Score:
-------------------
Killed Mutants
-------------------
-------------------
------------------- Total Mutants
-------------------
-------------------
-------------------
<
-------------------
39
Mutation analysis: Assessing the quality
of a test suite
Mutation testing: Improving the test
suite using mutants
40
Equivalent Mutants
Mutation = syntactic change
The change might leave
the semantics unchanged
Equivalent mutants are hard
to detect (undecidable problem)
Might be reached, but no infection
Might infect, but no propagation
41
Example 1
int max(int[] values) {
int r, i;
r = 0;
for(i = 1; i<values.length; i++) {
if (values[i] > values[r])
r = i;
}
return values[r];
}
42
In this mutant, the loop starts from
0 instead of 1. This mutant is
Example 1 equivalent because it only
introduces an additional
int max(int[] values) { comparison of the first element to
itself - this cannot change the
int r, i;
functional behavior.
r = 0;
for(i = 0; i<values.length; i++) {
if (values[i] > values[r])
r = i;
}
return values[r];
}
43
r = 0;
for(i = 1; i<values.length; i++) {
if (values[i] >= values[r])
r = i;
}
return values[r];
}
44
Example 1
int max(int[] values) {
int r, i;
r = 0;
for(i = 1; i<values.length; i++) {
if (values[r] > values[r])
r = i;
}
return values[r];
}
45
Example 2
if(x > 0) {
if(y > x) {
// ...
}
}
46
if(x > 0) {
if(y > abs(x)) {
// ...
}
}
47
Frankls Observation
We also observed that []
mutation testing was costly.
Even for these small subject programs,
the human effort needed to check a large
number of mutants for equivalence
was almost prohibitive.
Mutant Constraints
State infection can be represented as a
constraint system
Equivalent mutant problem = feasible path
problem
Heuristics can detect some infeasible path
constraints
Works for ~40% of equivalent mutants
50
Impact of mutations
A mutant is killed if an oracle checks one of
the places it propagates to
If a mutant propagates to many places,
chances of detecting it are higher
Impact = measurement of how much/far a
mutant propagates
High impact but not detected: Check your
oracles...
51
0,*!#@0A.(#-.(4#$%&!'(,#-.(.L!#$%&!'.(#-.(.7
!" #$%&!'()*&!+!(,#-.(./ A!
#$%&!'.)*&!+!.(#-.(./
0,*!-1!+!2/
9$0:(!4'()*&7!" B
#
>%:?( ;&<(
#$%&!#/ C
"
#!+!'()*&/
03!4#!++!5657!"!!
>%:?( ;&<(
$ %
!(:?(03!4#!++!5K57!" D 8!
'.)*&!+!5!5/
E
8 03!4.0@0*A$0@$!++!GH!II!.0@0*A:-9!++!GH7!"
Coverage Impact ;&<(
>%:?(
(:?(!" ( H -1!+!H/ I)
'.)*&!+!HJ!'!.0@0*A$0@$!6!.0@0*A:-9/ 8
8
52
Figure 12.2: The control flow graph of function cgi decode from Figure 12.1
Measurement
Number of methods with changed coverage
Number of methods with changed return values
Number of violated dynamic invariants
Killed Mutants
-------------------
-------------------
------------------- Total Mutants - Equivalent mutants
-------------------
-------------------
-------------------
<
-------------------
54
Example
Classify triangle by the length of the sides
!"#$$%&'()*%#+,"-(.'()/-("-+,)/$(0&()/-($%1-$(
55
int triangle(int a, int b, int c) { int triangle(int a, int b, int c) { Here we see the additional test
if (b <= 0
return
|| b <= 0 || c <= 0) {
4; // invalid
if (a <= 0
return
|| b <= 0 || c <= 0) {
4; // invalid
cases we need in order to kill these
}
}
if (! (a + b > c && a + c > b && b + c > a)) { if (! (a +
return
b > c && a + c > b && b + c > a)) {
4; // invalid
four mutants.
return 4; // invalid
} }
if (a == b && b == c) { if (a == b && b == c) {
return 1; // equilateral return 1; // equilateral
} }
if (a == b || b == c || a == c) { if (a == b || b == c || a >= c) {
}
return 2; // isosceles
}
return 2; // isosceles
(0, 0, 0)
return 3; // scalene return 3; // scalene
} }
if (a <= 0
return
|| b <= 0 || c <= 0) {
4; // invalid
if (b <= 0
return
|| b <= 0 || c <= 0) {
4; // invalid
(2, 2, 2)
} }
if (! (a * b > c && a + c > b && b + c > a)) { if (! (a + b > c && a + c > b && b + c > a)) {
return 4; // invalid return 4; // invalid
} }
if (a == b
return
&& b == c) {
1; // equilateral
if (a == b
return
&& b == c++) {
1; // equilateral
(2, 2, 3)
} }
if (a == b || b == c || a == c) { if (a == b || b == c || a == c) {
return 2; // isosceles return 2; // isosceles
} }
66
int triangle(int a, int b, int c) { int triangle(int a, int b, int c) { This is an equivalent mutant - we
if (b <= 0
return
|| b <= 0 || c <= 0) {
4; // invalid
if (a <= 0 || b <= 0 || c <= 0) {
return 4; // invalid
cannot kill it.
} }
b > c && a + c > b && b + c > a)) { if (! (a + b > c && a + c > b && b + c > a)) {
if (! (a +
return
4; // invalid return 4; // invalid
} }
if (a == b
&& b == c) { if (a == b && b == c) {
return
1; // equilateral return 1; // equilateral
} }
if (a == b
|| b == c || a == c) { if (a == b || b == c || a >= c) {
(0, 0, 0)
int triangle(int a, int b, int c)return
{ 2; // isosceles
return
2; // isosceles
} }
if (a <= 0 || b <= 0 || c <= 0) {
return 4; // invalid return 3; // scalene
return 3; // scalene
} }
}
}
(0, 1, 1)
if (! (a + b > c && a + c > b && b + c > a)) {
return 4; // invalid (4, 3, 2) (1, 1, 3)
if (a == b && b == c) {
return 1; // equilateral
}
int triangle(int a, int b, int c) { int triangle(int a, int b, int c) {
if (a == b || b == c || a++ == c) {
if (a <= 0
return 4; // invalid
}
return 2; // isosceles
|| b <= 0 || c <= 0) { if (b <= 0 || b <= 0 || c <= 0) {
return 4; // invalid
(2, 2, 2)
} }
if (! (a *
return
}
4; // invalid
return 3; // scalene
equivalent!
b > c && a + c > b && b + c > a)) { if (! (a + b > c && a + c > b && b + c > a)) {
return 4; // invalid
} }
if (a == b
return
&& b == c) {
1; // equilateral
if (a == b && b == c++) {
return 1; // equilateral
(2, 2, 3)
} }
if (a == b || b == c || a == c) { if (a == b || b == c || a == c) {
return 2; // isosceles return 2; // isosceles
} }
67
(0, 0, 0)
Performance
69
int triangle(int a, int b, int c) {
if (a <= 0 || b <= 0 || c <= 0) {
return 4; // invalid
}
if (! (a + b > c && a + c > b && b + c > a)) {
return 4; // invalid
}
if (a == b && b == c) {
return 1; // equilateral
}
if (a == b || b == c || a == c) {
return 2; // isosceles
}
return 3; // scalene
}
70
Performance Problems
Many mutation operators possible
Proteum - 103 Mutation Operators for C
MuJava - Adds 24 Class level Mutation Operators
Using Coverage
If we mutate the last if expression,
(0, 0, 0) then there is no point in executing
all of the test cases against the
int triangle(int a, int b, int c) { (1, 1, 3) mutants derived from the
expression. Only some of the test
if (a <= 0 || b <= 0 || c <= 0) { (2, 2, 2)
return 4; // invalid cases will actually execute the
} mutation. If a test case does not
if (! (a + b > c && a + c > b && b + c > a)) {
(2, 2, 3)
return 4; // invalid
execute the mutation, then there is
} (2, 3, 4) no way it could kill it. Therefore,
if (a == b && b == c) { before mutation analysis we
return 1; // equilateral
}
(0, 1, 1) determine statement coverage for
if (a == b || b == c || a == c) { each of the test cases, and during
return 2; // isosceles (4, 3, 2) mutation analysis only execute
} those test cases for a mutant that
Only these tests execute (1, 1, 1) actually reach the mutation.
return 3; // scalene
}
mutants in this line!
(2, 3, 2) 74
Weak mutation
Mutation has affected state (infection)
while(y != 0) { while(y != 0) {
tmp = x % y;
Weak tmp = x * y;
x = y; mutation x = y;
y = tmp; y = tmp;
} }
return x;
Strong return x;
} mutation }
76
79
Selective Mutation
Full Set:
Test cases that
kill all mutants
Sufficient Subset:
Test cases that kill
these mutants will
kill all mutants
80
Selective Mutation
Use only a subset of mutation operators
instead of all operators
Subset is sufficient
Detecting mutants of sufficient subset will
detect >99% of all mutants
ABS, AOR, COR, ROR, UOI
81
Do Smarter/Faster
6: iload_1 // a
7: ifeq 14
10: iload_2 // b
11: ifne 23
Mutating bytecode
avoids recompilation
82
83
Only a small
proportion of the
FOMs are likely
to simulate real
faults.
84
------------------- ------------------- ------------------- -------------------
------------------- -------------------
+ ------------------- -------------------
+
------------------- ------------------- ------------------- -------------------
------------------- ------------------- ------------------- -------------------
------------------- ------------------- ------------------- -------------------
------------------- ------------------- -------------------
< -------------------
<
------------------- ------------------- ------------------- -------------------
85
Mutants FOMs
Domain
HOMs
86
HOM classification
Most common case
1
2 3
Tab
Ta 4 5
6
Tb
Strongly Subsuming 1
2 3Tab
Ta 4 5
6
Tb
Strongly Subsuming 1
2 3Tab
De-Coupled Ta 4 5
6
Tb
Types of HOMs
Strongly Subsuming 1
2 3
De-Coupled Ta 4 5
6
Equivalent Tb
HOM Conclusions
Many HOMs are simple to kill (coupling
effect)
But some HOMs are very interesting
Because there are so many HOMs, there
are many interesting ones as well
The ratio of equivalent HOMs is better
than for FOMs
How to get good HOMs? Open research
problem
98
Mutation vs.
Statement Coverage
int gcd(int x, int y) {
int tmp;
while(y != 0) {
tmp = x % y;
x = y;
y = tmp;
}
while(yfalse
!= 0) {
true
tmp = x % y;
x = y;
y = tmp;
}
Estimating #Defects
102
Lets consider a lake. How many
fish are in that lake?
103
50
300
105
and we can thus estimate that
there are about 6,000 remaining
Estimate untagged fish in the lake.
1,000 50
=
untagged fish population 300
106
107
Program
108
Simple. We catch a number of fish
(say, 1000), tag them, and throw
A Mutant them back again.
50
300
110
1,000 50
=
remaining defects 300
111
112