Das Spock Testframework verwendet das ausdrucksstarke Groovy um BDD als Testansatz zu realisieren. Neben einer klaren Teststruktur punktet es mit lesbaren Reports, die dank einer Template-Engine auch in AsciiDoc generiert werden können. Kann man das noch verbessern? Wir treten den Beweis an und kombinieren Spock mit Mutationstests. Denn eine Testabdeckung von über 80 % beweist noch nicht, dass die Testsuite in der Lage ist Fehler zuverlässig zu erkennen. Der Vortrag zeigt, wie sich Spock mit Mutationstesten zu einem cleveren Duo kombinieren lässt.
4. Was ist Spock?
4
private Fibonacci fib;
@Before
public void setup() {
fib = new Fibonacci();
}
@Test
public void seedValue0() {
assertEquals(0, fib.calc(0));
}
@Test
public void seedValue1() {
assertEquals(1, fib.calc(1));
}
// …
Developer
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
5. Was ist Spock?
5
private Fibonacci fib;
@Before
public void setup() {
fib = new Fibonacci();
}
@Test
public void seedValue0() {
assertEquals(0, fib.calc(0));
}
@Test
public void seedValue1() {
assertEquals(1, fib.calc(1));
}
// …
Developer
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
6. Was ist Spock?
6
private Fibonacci fib;
@Before
public void setup() {
fib = new Fibonacci();
}
@Test
public void seedValue0() {
assertEquals(0, fib.calc(0));
}
@Test
public void seedValue1() {
assertEquals(1, fib.calc(1));
}
// …
Developer
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
7. Was ist Spock?
7
Fibonacci fib;
@Before
void setup() {
fib = new Fibonacci();
}
void seedValue0() {
assertEquals(0, fib.calc(0));
}
void seedValue1() {
assertEquals(1, fib.calc(1));
}
// …
Developer
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
8. Was ist Spock?
8
Fibonacci fib;
@Before
void setup() {
fib = new Fibonacci();
}
void "test the fibonacci generator with input 0"() {
assertEquals(0, fib.calc(0));
}
void "test the fibonacci generator with input 1" {
assertEquals(1, fib.calc(1));
}
// …
Developer
Product
Owner
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
9. Was ist Spock?
9
Fibonacci fib;
@Before
void setup() {
fib = new Fibonacci();
}
void "test the fibonacci generator with input 0"() {
given:
Fibonacci fib = new Fibonacci()
when:
def result = fib.calc(i)
then:
assertEquals(0, fib.calc(0));
}
void "test the fibonacci generator with input 1" {
assertEquals(1, fib.calc(1));
}
// …
Developer
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
10. Was ist Spock?
10
void "test the fibonacci generator with input 0"() {
given:
Fibonacci fib = new Fibonacci()
when:
def result = fib.calc(i)
then:
assertEquals(0, fib.calc(0));
}
void "test the fibonacci generator with input 1" {
assertEquals(1, fib.calc(1));
}
// …
Developer
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
11. Was ist Spock?
11
void "test the fibonacci generator with input 0"() {
given: "an instance of the fibonacci generator"
Fibonacci fib = new Fibonacci()
when: "the fib sequence for 0 is calculated"
def result = fib.calc(i)
then: "the expected number is returned"
assertEquals(0, fib.calc(0));
}
void "test the fibonacci generator with input 1" {
assertEquals(1, fib.calc(1));
}
// …
Developer
Product
Owner
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
12. Was ist Spock?
12
void "test the fibonacci generator with input 0"() {
given: "an instance of the fibonacci generator"
Fibonacci fib = new Fibonacci()
when: "the fib sequence for 0 is calculated"
def result = fib.calc(i)
then: "the expected number is returned"
result == expected
}
void "test the fibonacci generator with input 1" {
assertEquals(1, fib.calc(1));
}
// …
Developer
Product
Owner
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
13. Was ist Spock?
13
void "test the fibonacci generator with input 0"() {
given: "an instance of the fibonacci generator"
Fibonacci fib = new Fibonacci()
when: "the fib sequence for a given input #i is calculated"
def result = fib.calc(i)
then: "the expected number #expected is returned"
result == expected
where:
i || expected
0 || 0
2 || 1
11 || 89
}
Developer
Product
Owner
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst
14. DB Systel. Digital bewegen. Gemeinsam. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst14
Reports
15. DB Systel. Digital bewegen. Gemeinsam. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst15
Reports
16. DB Systel. Digital bewegen. Gemeinsam. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst16
Reports
Product
Owner
Developer
17. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst17
100%
Zeilenabdeckung?
18. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst18
100%
Icons made by Sprang from www.flaticon.com
19. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst19
Finde den Fehler?
public List<Integer> sort(List<Integer> coll) {
List<Integer> list = new ArrayList<>(coll);
Collections.sort(list);
log(list);
return Collections.unmodifiableList(list);
}
public void log(List<Integer> list) {
System.out.println(
list.stream().map(Object::toString)
.collect(Collectors.joining(", ")));
}
Icons made by Sprang from www.flaticon.com
20. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst20
Finde den Fehler?
def "test Sort"() {
given: "an instance of Sort"
def Sort = new Sort()
when: "the given list #list is sorted"
def result = Sort.sort(list)
then: "the result is as #expected"
result == expected
where: ""
list || expected
[] || []
[5] || [5]
[2,1,3,8] || [1,2,3,8]
}
Icons made by Sprang from www.flaticon.com
21. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst21
Finde den Fehler?
public List<Integer> sort(List<Integer> coll) {
List<Integer> list = new ArrayList<>(coll);
Collections.sort(list);
log(list);
return Collections.unmodifiableList(list);
}
public void log(List<Integer> list) {
System.out.println(
list.stream().map(Object::toString)
.collect(Collectors.joining(", ")));
}
Icons made by Sprang from www.flaticon.com
22. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst22
Und nun?
Icons made by Sprang from www.flaticon.com
23. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst23
Fehler gezielt provozieren
Icons made by Sprang from www.flaticon.com
24. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst24
Automatisierung
Icons made by Sprang from www.flaticon.com
25. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst25
Mutationstesten
Icons made by Sprang from www.flaticon.com
26. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst26
Mutationstesten - Funktionsweise
Icons made by Sprang from www.flaticon.com
27. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst27
DEMO HTML
Report
Icons made by Sprang from www.flaticon.com
28. Increments Mutator
i++ wird zu i--
Math Mutator
+ wird zu -
* wird zu /
Negate Conditionals Mutator
== wird zu !=
<= wird zu >
Conditionals Boundary Mutator
< wird zu <=
<= wird zu <
Invert Negs Mutator
-i wird zu i
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst28
Default Mutatoren
29. Increments Mutator
i++ wird zu i--
Math Mutator
+ wird zu -
* wird zu /
Negate Conditionals Mutator
== wird zu !=
<= wird zu >
Conditionals Boundary Mutator
< wird zu <=
<= wird zu <
Invert Negs Mutator
-i wird zu i
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst29
Default Mutatoren
30. Increments Mutator
i++ wird zu i--
Math Mutator
+ wird zu -
* wird zu /
Negate Conditionals Mutator
== wird zu !=
<= wird zu >
Conditionals Boundary Mutator
< wird zu <=
<= wird zu <
Invert Negs Mutator
-i wird zu i
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst30
Default Mutatoren
31. Increments Mutator
i++ wird zu i--
Math Mutator
+ wird zu -
* wird zu /
Negate Conditionals Mutator
== wird zu !=
<= wird zu >
Conditionals Boundary Mutator
< wird zu <=
<= wird zu <
Invert Negs Mutator
-i wird zu i
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst31
Default Mutatoren
32. Increments Mutator
i++ wird zu i--
Math Mutator
+ wird zu -
* wird zu /
Negate Conditionals Mutator
== wird zu !=
<= wird zu >
Conditionals Boundary Mutator
< wird zu <=
<= wird zu <
Invert Negs Mutator
-i wird zu i
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst32
Default Mutatoren
33. public Object foo() {
return new Object();
}
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst33
Code Smells finden mit dem Return Values Mutator
public Object foo() {
new Object();
return null;
}
34. public Object foo() {
return new Object();
}
Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst34
Code Smells finden mit dem Return Values Mutator
public Object foo() {
new Object();
return null;
}
35. DB Systel. Digital bewegen. Gemeinsam. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst35
Dead Code finden mit dem Void Method Call Mutator
public List<Integer> sort(List<Integer> coll) {
List<Integer> list = new ArrayList<>(coll);
Collections.sort(list);
log(list);
return Collections.unmodifiableList(list);
}
public void log(List<Integer> list) {
System.out.println(
list.stream().map(Object::toString)
.collect(Collectors.joining(", ")));
}
Icons made by Sprang from www.flaticon.com
36. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst36
Äquivalente Mutationen
public int calc(int i) {
if (i == 0) {
return 0;
}
if (i <= 2) {
return 1;
}
return
calc(i-1) + calc(i-2);
}
def "test fibonacci generator"() {
given: "fibonacci generator"
Fibonacci fib = new Fibonacci()
when: "calc sequence for input #i"
def result = fib.calc(i)
then: "expected number is returned"
result == expected
where:
i || expected
0 || 0
2 || 1
11 || 89
}
i < 2
Icons made by Sprang from www.flaticon.com
37. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst37
Warum PIT?
Schnell durch
Parallelisierung
Arbeitet auf
Bytecode
Lesbare
Reports
Tooling
38. DB Systel. Digital bewegen. Gemeinsam. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst38
39. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst39
Mutationstesten – im größeren Team
Icons made by Sprang from www.flaticon.com
40. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst40
Mutationstesten – im größeren Team
Icons made by Sprang from www.flaticon.com
44. Welche zwei Fragen sind noch offen?
Johannes.Dienst@DeutscheBahn.com
@JohannesDienst
Ralf.D.Mueller@DeutscheBahn.com
@RalfDMueller
45. DB Systel. Digital bewegen. Gemeinsam. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst45
https://jaxenter.de/mutant-testing-pit-java-84437
https://m.heise.de/developer/artikel/Mutationstests-mit-PIT-in-
Java-3888683.html?seite=all