Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

Ralf D. Müller

July 23, 2020
Tweet

More Decks by Ralf D. Müller

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. DB Systel. Digital bewegen. Gemeinsam. Ralf D. Müller @RalfDMueller und

    Johannes Dienst @JohannesDienst 16 Reports Product Owner Developer
  12. 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
  13. 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
  14. 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
  15. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 22 Und

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

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

    - Funktionsweise Icons made by Sprang from www.flaticon.com
  18. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 27 DEMO

    HTML Report Icons made by Sprang from www.flaticon.com
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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; }
  25. 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; }
  26. 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
  27. 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
  28. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 37 Warum

    PIT? Schnell durch Parallelisierung Arbeitet auf Bytecode Lesbare Reports Tooling
  29. Ralf D. Müller @RalfDMueller und Johannes Dienst @JohannesDienst 39 Mutationstesten

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

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

  32. 42

  33. 43

  34. 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