Spock vs Supermutanten: Spezifikationstesten trifft Mutationstesten

Spock vs Supermutanten: Spezifikationstesten trifft Mutationstesten

SAEC Days 2020. online, 23.7.2020

https://www.saec-days.de/agenda/vortraege/articles/do33-spock-vs-supermutanten-spezifikationstesten-trifft-mutationstesten.html

Das Spock Testframework verwendet das ausdrucksstarke Groovy um Behaviour Driven Development (BDD) als Testansatz zu realisieren. Neben einer klaren und schlanken Teststruktur punktet es außerdem mit lesbaren Reports, die dank einer Template-Engine auch in AsciiDoc generiert werden können. Verfolgt man BDD mit Spock konsequent während der Entwicklung, dann entsteht eine Testsuite, die eine sehr hohe Zeilenabdeckung von über 80% erzeugt.

Allgemein anerkannt ist, dass eine so hohe Testabdeckung ein gutes Sicherheitsnetz bei Änderungen am Code darstellt.

Doch diese Sicherheit trügt, wie wir im zweiten Teil des Vortrags zeigen werden. Denn eine hohe Zeilenabdeckung schützt nicht vor Fehlern, wenn die Tests eine niedrige Qualität vorweisen. Hier kommt Mutationstesten ins Spiel. Es sät automatisiert Mutationen in den Code und führt die Testsuite aus. Schlägt mindestens ein Test fehl ist die Mutation getötet. Die Güte der Tests kann anschließend aus der Prozentzahl der getöteten Mutanten ermittelt werden.

Der Vortrag zeigt, wie sich Spock mit Mutationstesten zu einem unschlagbaren Duo kombinieren lässt.

http://spockframework.org/
http://pitest.org/
https://github.com/JohannesDienstDBSystel/spock-mutation-testing

Cc5f3bf8b3cb91c985ed4fd046aa451d?s=128

Ralf D. Müller

July 23, 2020
Tweet

Transcript

  1. Ralf D. Müller @RalfDMueller Johannes Dienst @JohannesDienst Spock vs Supermutanten

    Spezifikationstesten trifft auf Mutationstesten
  2. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst Was ist

    Spock?
  3. Was ist Spock? 3 Leonard Brüning Ralf D. Müller @RalfDMueller

    und Johannes Dienst @JohannesDienst
  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, result); } 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, result); } 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, result); } 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 == 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
  13. Was ist Spock? 13 void "test the fibonacci generator with

    input #i"() { 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 @JohannesDienst 14 Reports
  15. DB Systel. Digital bewegen. Gemeinsam. Ralf D. Müller @RalfDMueller und

    Johannes Dienst @JohannesDienst 15 Reports
  16. DB Systel. Digital bewegen. Gemeinsam. Ralf D. Müller @RalfDMueller und

    Johannes Dienst @JohannesDienst 16 Reports Product Owner Developer
  17. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 17 Zeilenabdeckung?

  18. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 18 100%

    Icons made by Sprang from www.flaticon.com
  19. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 19 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 @JohannesDienst 20 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 @JohannesDienst 21 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 @JohannesDienst 22 Und

    nun? Icons made by Sprang from www.flaticon.com
  23. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 23 Fehler

    gezielt provozieren Icons made by Sprang from www.flaticon.com
  24. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 24 Automatisierung

    Icons made by Sprang from www.flaticon.com
  25. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 25 Mutationstesten

    Icons made by Sprang from www.flaticon.com
  26. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 26 Mutationstesten

    - Funktionsweise Icons made by Sprang from www.flaticon.com
  27. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 27 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 @JohannesDienst 28 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 @JohannesDienst 29 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 @JohannesDienst 30 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 @JohannesDienst 31 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 @JohannesDienst 32 Default Mutatoren
  33. public Object foo() { return new Object(); } Ralf D.

    Müller @RalfDMueller und Johannes Dienst @JohannesDienst 33 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 @JohannesDienst 34 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 @JohannesDienst 35 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 @JohannesDienst 36 Ä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 @JohannesDienst 37 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 @JohannesDienst 38
  39. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 39 Mutationstesten

    – im größeren Team Icons made by Sprang from www.flaticon.com
  40. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 40 Mutationstesten

    – im größeren Team Icons made by Sprang from www.flaticon.com
  41. 41

  42. 42

  43. 43

  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 @JohannesDienst 45 https://jaxenter.de/mutant-testing-pit-java-84437 https://m.heise.de/developer/artikel/Mutationstests-mit-PIT-in- Java-3888683.html?seite=all