dial m for mutation
TRANSCRIPT
JavaZone 2016
www.slideshare.net/filipvanlaenen/dial-m-for-mutation
Filip van [email protected]@filipvanlaenen
Dial M for Mutation
Mutation Testing
• Unit tests testing the source code
• Make a change to the source code• Failing unit test
16
Mutation Testing
• Unit tests testing the source code
• Make a change to the source code• Failing unit test• Many failing unit tests
17
Mutation Testing
• Unit tests testing the source code
• Make a change to the source code• Failing unit test• Many failing unit tests• Infinite loops, compilation
errors, run-time errors, etc…
18
Mutation Testing Tools
• PITest• Java• http://pitest.org/
• Mutant• Ruby• https://github.com/mbj/mutant
20
Getters and Setters
• How difficult can it be?
• If I had a dime for every time…
22 https://www.flickr.com/photos/mussikatz/16015546324/
Detach from the Input
• Isolate inner state from input parameters
• Detach from input parameters• Arrays• Collections• Objects in general
23 https://www.flickr.com/photos/macieksz/503731332/
Detach the Output
• Don’t expose internal state• (Deep) copies• Unmodifiable collections
24 https://www.flickr.com/photos/kelloggphotography/2099315965/
Unit Testing Getters, Setters, Constructors and Builders
• Are getters, setters, constructors and builders wired together correctly?• Primitive types, but also objects
• Are setters, constructors and builders detaching correctly from input parameters?• Arrays, collections, objects
• Are getters returning (deep) copies or unmodifiable versions?
25
Unit Testing a Square
• How many unit tests do you need?
27 https://www.flickr.com/photos/jasoncooper/13204428724/
Unit Testing a Square
• How many unit tests do you need?• 0?
28 https://www.flickr.com/photos/jasoncooper/13204428724/
Unit Testing a Square
• How many unit tests do you need?• 0?• 1?
29 https://www.flickr.com/photos/jasoncooper/13204428724/
Unit Testing a Square
• How many unit tests do you need?• 0?• 1?• 2?
30 https://www.flickr.com/photos/jasoncooper/13204428724/
Unit Testing a Square
• How many unit tests do you need?• 0?• 1?• 2?• 4?
31 https://www.flickr.com/photos/jasoncooper/13204428724/
Unit Testing a Square
• How many unit tests do you need?• 0?• 1?• 2?• 4?• 8?
32 https://www.flickr.com/photos/jasoncooper/13204428724/
[-1, 1] × [-1, 1]
KISS implementation:
boolean isInside(double x, double y) {
return -1 <= x && x <= 1 && -1 <= y && y <= 1;
}
35
[-1, 1] × [-1, 1]
KISS implementation:
boolean isInside(double x, double y) {
return -1 <= x && x <= 1 && -1 <= y && y <= 1;
}
36
“Naive” Unit Tests
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
→ 9 out of 17 mutants killed
38
“Naive” Unit Tests
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
→ 9 out of 17 mutants killed
39
“Naive” Unit Tests
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
→ 9 out of 17 mutants killed
40
Adding a Corner Case
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
41
Adding a Corner Case
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
→ 11 (+2) out of 17 mutants killed
42
Adding Corner Cases
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
43
Adding Corner Cases
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
→ 13 (+2) out of 17 mutants killed
44
Adding Corner Cases
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
→ 13 (+2) out of 17 mutants killed
45
Adding Corner Cases
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
→ 13 (+2) out of 17 mutants killed
46
Conditions in Isolation
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
47
Conditions in Isolation
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
→ 14 (+1) out of 17 mutants killed
48
Conditions in Isolation
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
49
Conditions in Isolation
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
→ 15 (+1) out of 17 mutants killed
50
Conditions in Isolation
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
51
Conditions in Isolation
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
→ 16 (+1) out of 17 mutants killed52
Conditions in Isolation
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
assertFalse(square.isInside(0, -2));
53
Conditions in Isolation
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
assertFalse(square.isInside(0, -2));
54
“Naive” Unit Tests?
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
assertFalse(square.isInside(0, -2));
55
Useless Unit Tests
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
assertFalse(square.isInside(0, -2));
56
Useless Unit Tests
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
assertFalse(square.isInside(0, -2));
57
Useless Unit Tests
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
assertFalse(square.isInside(0, -2));
58
Six Unit Tests
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
assertFalse(square.isInside(0, -2));
59
Unit Testing a Square
• How many unit tests do you need?• 0?• 1?• 2?• 4?• 8?• 6
60 https://www.flickr.com/photos/jasoncooper/13204428724/
Unit Testing a Square
• How many unit tests do you need?• 0?• 1?• 2?• 4?• 8?• At least 6
61 https://www.flickr.com/photos/jasoncooper/13204428724/
[-1, 1] × [-1, 1]
KISS implementation:
boolean isInside(double x, double y) {
return -1 <= x && x <= 1 && -1 <= y && y <= 1;
}
62
[-1, 1] × [-1, 1]
A mutant (currently) not generated by PIT:
boolean isInside(double x, double y) {
return -1 <= x && Math.floor(x) <= 1 && -1 <= y && y <= 1;
}
63
[-1, 1] × [-1, 1]
A mutant (currently) not generated by PIT:
boolean isInside(double x, double y) {
return -1 <= x && Math.floor(x) <= 1 && -1 <= y && y <= 1;
}
64
[-1, 1] × [-1, 1]
A mutant (currently) not generated by PIT:
boolean isInside(double x, double y) {
return -1 <= x && Math.floor(x) <= 1 && -1 <= y && y <= 1;
}
65
Binding the Square
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-2, 0));
assertFalse(square.isInside(0, 2));
assertFalse(square.isInside(2, 0));
assertFalse(square.isInside(0, -2));
68
Binding the Square
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-1.01, 0));
assertFalse(square.isInside(0, 1.01));
assertFalse(square.isInside(1.01, 0));
assertFalse(square.isInside(0, -1.01));
69
Unit Testing a Square
• How many unit tests do you need?• 0?• 1?• 2?• 4?• 8?• At least 6
70 https://www.flickr.com/photos/jasoncooper/13204428724/
Testing Conditionals
• Involves three things:• Exercising the conditional• Running the conditional
clause• Verifying the functionality
of the conditional clause
73 https://www.flickr.com/photos/borkurdotnet/6797142125/
Testing Conditionals
• Involves three things:• Exercising the conditional• Running the conditional
clause• Verifying the functionality
of the conditional clause
If you can’t reach the clause, consider removing the code!74 https://www.flickr.com/photos/borkurdotnet/6797142125/
Mutating Conditionals
• Three ways to mutate a conditional:• Negate• Replace with false• Replace with true
75
Mutating Conditionals
• Three ways to mutate* a conditional:• Negate• Replace with false• Replace with true
* Or get it wrong
76
Mutating Conditionals
• Three ways to mutate* a conditional:• Negate• Replace with false• Replace with true
* Or get it wrong
77
Conditions in Isolation
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-1.01, 0));
79
Testing Log Messages
• Why shouldn’t you?
• Ways to get log messages wrong:• Absent• Wrong log level• Wrong message content• Exceptions
82 https://www.flickr.com/photos/waltstoneburner/7946581522/
How to Test Logging
• Create an in-memory appender
• Set up the condition for the message to be logged
• Check for:• Its presence• Log level• Message content
83 https://www.flickr.com/photos/waltstoneburner/7946581522/
How to Test Exceptions
• Set up the condition for the exception to be thrown
• Check for:• Its presence• Exception type• Message content
84 https://www.flickr.com/photos/7304439@N06/1453921573/
Constant Constants
• Some constants are pretty constant:• 0, 1, 2, e, π, c, …
• Others may change:• 18 (age of majority)• 16, 21, 25, …
• Or are magic numbers :• 42, population sizes, …
87 https://www.flickr.com/photos/theilr/16621361510/
Reuse or Redefine?
• Constant constants:• Reuse
• Changing constants:• Redefine
• Magic numbers:• Redefine• Double-entry
bookkeeping88 https://www.flickr.com/photos/52814185@N02/6643480765/
Unit Testing Constants
• Constant constants:• Implicitly
• Changing constants:• Implicitly
• Magic numbers:• Explicitly
89 https://www.flickr.com/photos/quasimondo/97503572
Testing Public APIs
• Functionality ≠ public API
• Testing in the same package:• Common practice• JUnit, TestNG, …• Maven conventions• IDEs
• “Interface exhibitionism”91 https://www.flickr.com/photos/neesam/28674222645/
Testing Public APIs
• Test functionality in the same package• Use the default access
modifier when needed
• Test the public API from a different package• To figure out which
interfaces, classes and methods should be public
92 https://www.flickr.com/photos/neesam/28674222645/
Access Modifier Mutator
• I could wish for an access modifier mutator:• On every interface, class,
method, field, etc…• public → protected• protected → (package)• (package) → private
93 https://www.flickr.com/photos/neesam/28674222645/
Useless Unit Tests
assertTrue(square.isInside(0, 0));
assertFalse(square.isInside(2, 2));
assertTrue(square.isInside(1, 1));
assertTrue(square.isInside(-1, -1));
assertFalse(square.isInside(-1.01, 0));
assertFalse(square.isInside(0, 1.01));
assertFalse(square.isInside(1.01, 0));
assertFalse(square.isInside(0, -1.01));
97
JavaZone 2016
www.slideshare.net/filipvanlaenen/dial-m-for-mutation
Filip van [email protected]@filipvanlaenen
Questions?Comments?