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

Property Based Testing

Property Based Testing

Property Based Testing (PBT) ist ein Testvorgehen, das normale, Beispiel-getriebene Tests ergänzt, indem Eigenschaften des zu testenden Codes durch Generierung von Testdaten überprüft werden.

PBT kommt aus der Ecke der funktionalen Programmierung und ist in der imperativen Welt noch nicht weit verbreitet. Daher möchte Daniel euch gerne zeigen, wie ihr PBT in euren Projekten einsetzten könnt, welche Vorteile es euch bringt und was die Fallstricke sind, auf die man achten muss.

Daniel Heinrich

April 13, 2021
Tweet

More Decks by Daniel Heinrich

Other Decks in Programming

Transcript

  1. Inhalt • Automatische Software Tests • Property Based Testing •

    Beispiel • Frameworks • Properties • Fazit
  2. Tests “To test is to stimulate a system and observe

    its response.” [Barr et al. 2015] var output = operation(input) assertIsCorrect(input, output); “To test is to stimulate a system and observe its response.” [Barr et al. 2015] Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit • Man testet, da Korrektheit nicht bewiesen werden kann • Für Zuversicht braucht man viele Tests der selben Operation
  3. Orakel Problem Testvorrausetzung • Input • assertIsCorret(…) aka. Test Orakel

    Gängigste Test Orakel sind definierte Input/Output Paare. Problem der automatischen Testerzeugung • Inputs können zwar generiert werden, z.B. IntStream.range(1, 42) • Erzeugung eins vollständigen Orakels aber nicht möglich Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  4. Automatische Test Orakel • Specified • Formale Spezifikation (Algebra, UML,

    Schema - SoapUI) • Derived • Dokumentation, Code Annotationen • aufgezeichnetes Laufzeitverhalten (z.B. Wiremock) • Contract Drive Development • Implicit • Bufferoverflow, Exception • Load Tests • Memory Leaks • Security • Pseudo • Metamorphic • Unterschiedliche Implementierungen (u.a. vorherige Version, Alternative, Model) [Barr et al. 2015] • Specified • Formale Spezifikation (Algebra, UML, Schema - SoapUI) • Derived • Dokumentation, Code Annotationen • aufgezeichnetes Laufzeitverhalten (z.B. Wiremock) • Contract Drive Development • Implicit • Bufferoverflow, Exception • Load Tests • Memory Leaks • Security • Pseudo • Metamorphic • Unterschiedliche Implementierungen (u.a. vorherige Version, Alternative, Model) • Specified • Formale Spezifikation (Algebra, UML, Schema - SoapUI) • Derived • Dokumentation, Code Annotationen • aufgezeichnetes Laufzeitverhalten (z.B. Wiremock) • Contract Drive Development • Implicit • Bufferoverflow, Exception • Load Tests • Memory Leaks • Security • Pseudo • Metamorphic • Unterschiedliche Implementierungen (u.a. vorherige Version, Alternative, Model) Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  5. Fuzzing Theories Com bination Testing Automatische Software Tests ◦ Property

    Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  6. PBT Orakel [Kerr 2014] Input Output Automatische Software Tests ◦

    Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit Generierte Inputs + Überprüfung der Eigenschaften der Outputs
  7. Property Based Testing QuickCheck (2000) John Hughes and Koen Clasessen

    “Property based testing is the construction of tests such that, when these tests are fuzzed, failures in the test reveal problems with the system under test that could not have been revealed by direct fuzzing of that system.” [MacIver 2016] Fuzzing: mit generierten Inputs ein Laufzeitfehler provozieren PBT: mit generierten Inputs ein fehlschlagenden Test provozieren Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  8. Beispiel: Sortierung @Test void shouldBeSorted() { var sorted = sort(List.of(3,

    1, 2)); assertEquals(sorted, List.of(1, 2, 3)); } Edge Cases • (un)gerade Anzahl an Elementen • erstes/letztes liegt falsch • 0, 1, 2, 3 Elemente • große Liste • stabile Sortierung • doppelte Einträge Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  9. Beispiel: Sortierung @Test void shouldBeSorted() { var sorted = sort(List.of(3,

    1, 2)); assertEquals(sorted, List.of(1, 2, 3)); } @Property void shouldBeSorted(@ForAll List<Integer> input) { var sorted = sort(input); assertEquals(sorted, ???); } @Property void shouldBeSorted(@ForAll List<Integer> input) { var sorted = sort(input); assertIsSorted(sorted); } @Property void shouldHaveSameElements(@ForAll List<Integer> input) { var sorted = sort(input); assertSameElements(input, sorted); } Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  10. PBT in der Praxis Für alle Programmiersprachen: fast-check (JS), Hypothesis

    (Python), jqwik (Java), ScalaCheck, SwiftCheck Komponenten • Generatoren • Shrinker • Property DSL • Reporting (u.a. Statistiken) Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  11. Generatoren • Vgl. Pseudo Random Number Generator • Unendliche Sequenz

    an Werten • Ein Seed definiert die Sequenz • Default Generatoren für gängige Typen • List, Array, Map • Primitive, String, Date … • Kombinier- Transformierbar (map, filter, …) • Nur gerade Zahlen: strings().map(x -> x + “ “ + x) • Komplexe Objekte: combine(integers(), string()).as(Person::new) Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  12. Generatoren Generatoren erstellen Typspezifische Edge-Cases • Int: kleine Zahlen, MAX_VALUE,

    MIN_VALUE • List: eher kleine Listen • String: seltene Unicode Symbole Kein reiner Zufall, sondern Auswahl von interessanten Werten und Kombinationen Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  13. Generatoren • Generiert Generator wichtige Testfälle? • Generator testen, bzw.

    Statistiken beachten • z.B. custom Generator für Text (Infinite-Monkey-Theorem) • Filtern von Generatoren vermeiden • integers().map(i -> i % 10) besser als integers().filter(i -> i >= 0 && i < 10) • Große Generatoren sind langsam z.B. Map<Company, List<Person>> Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  14. Shrinker • shouldBeSorted failed with sample [[-1444396823, 2, 1745383257, -

    337822824, -1303117246, -251390176, -2, -166, -20, -806982505, - 972534736, 310239030, -652951279, 1, 2, -3, -5, 10, 1275032325, - 2116029871, -3, -2, 108, 148, 30, -1210442893, 97488720, 1, -38, - 461, 42]] • shouldBeSorted failed with sample [[0, -1]] Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  15. Shrinker Nach einem gefundenen Fehler, wird das Sample so lange

    verkleinert, bis der Fehler nicht mehr auftritt. Beispiel: Sortierung ist absteigend implementiert 1. Fehler wird erstmals bei einer großer Liste gefunden [32235, -23456256, -3, 363666, 19356] 2. Bei zwei Elemente tritt der Fehler immernoch auf [-3, 363666] 3. Der Fehler tritt nur bei zwei unterschiedlichen Werten auf [-1, 0] Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  16. Beispiel: Sortierung • Zwei weitere Properties für Prüfung von stabile

    Sortierung • Shell-Sort (hierarchischer Insertion-Sort) • Insertion-Sort ist stabil, Shell-Sort nicht • Aber erst ab 6 Elementen (je nach Parameter) Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  17. Properties Bilbo Testing (aka, There And Back Again) Findet Probleme

    mit • null/undefined Werten • Datum Format (Zeitumstellung) • abgeschnittene Werte in • Serialize/Deserialize & Converter • Undo & Redo operation inverse Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  18. Properties Unterschiedlicher Weg, selbes Ziel sort sort remove 2 [3,

    -1, 2, 4] [-1, 2, 3, 4] [3, -1, 4] [-1, 3, 4] remove 2 Bei unabhängige Operationen oder wenn Reihenfolge irrelevante. z.B. Reihenfolge von Coupons hat keine Auswirkung auf den finalen Preis Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  19. Properties Modell basiert Operation abstrakte Operation abstrahieren abstrahieren DB Tabelle

    -> insert(k, v) -> toMap() -> Map DB Tabelle -> toMap() -> put(k, v) -> Map Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  20. Properties • “No Unexpected Changes” / Invariant • Baum bleibt

    nach jeder Operation balanciert • Entity behält ID nach update • “Hard to Solve, Easy to Verify” • Sortierung, Primzahl, Sudoku, Terminplanung • Mathematische Gesetzte • Kommutativ, Idempotent, Identity … • Technische Korrektheit • Exceptions in SerDe, 5xx in HTTP Endpunkt, SQL Schema • Business Rule (Spezifikation) • Alternative Implementierung [Steinhauser 2018] Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  21. How to specify it Empirische Untersuchung von John Hughes •

    Postconditions (Relate return values to arguments of a single call.) • Metamorphic (Related calls return related results.) • Sehr ergiebig, bei N Operationen N2 Properties • Nur eine Näherung => beschreibt das Verhalten nicht komplett • Modell basiert • Komplette Spezifikation • Nur wenn Model einfach zu definieren ist • Wenn Model zu ähnlich zur Implementierung => Tautologie [Hughes 2019] Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  22. How to specify it Modell basiert • Ein Property pro

    Operation • Findet Fehler schnell • Modell nicht immer verfügbar Metamorphic • Properties sind leicht zu finden • Viele Properties für gute Abdeckung benötigt Postconditions • Properties überschneiden sich stark • Finden Fehler am langsamsten [Hughes 2019] Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  23. Eigene Erfahrungen Microservice mit vielen CRUD APIs: • Upserts/Remove von

    externen Aggregates • Ein eigenes großes Aggregate • Suchen • Daten/Relationen vom User • Wenige Commands: z.B. tausche Relationen Erkenntnisse • Kaum Properties bei CRUD • Kein Modell fürs gesammte System • Großes Aggregate bleibt nach Shrinken auch groß • Fuzzing von DB-Integration u. JSON-Parser • Symetry von SerDes • Generator in “normalen” Tests nutzen Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  24. Zusammenfassung • Neben Postconditions gibt es viele weiter Arten Tests

    zu schreiben • Beim PBT muss man sich keine Edge-Cases überlegen • Properties bilden eine fachliche Spezifikation • Menschen denken in Beispielen, daher gut als Dokumentation • Mehr Freiraum in der Implementierung • Beispiel Tests funktionieren nach einem Refactoring • Property Based Tests funktionieren nach geänderten Verhalten (so lange im fachlichen Rahmen) • Das schreiben von PBT ist ein aktiver Prozess • Sind die fachlichen Annahmen korrekt, wiedersprechen sie sich? • Man wird auf Edge-Cases hingewiesen (sin(x) immer zwischen -1;1? Was ist mit NaN?) • 90% der fehlschlagen Tests beruhen auf Fehler im Test, da Annahmen falsch waren. Automatische Software Tests ◦ Property Based Testing ◦ Beispiel ◦ Frameworks ◦ Properties ◦ Fazit
  25. Quellen • [Barr et al. 2015] E. T. Barr, M.

    Harman, P. McMinn, M. Shahbaz and S. Yoo, "The Oracle Problem in Software Testing: A Survey," in IEEE Transactions on Software Engineering, vol. 41, no. 5, pp. 507-525, 1 May 2015, doi: 10.1109/TSE.2014.2372785. • [Kerr 2014] Jessica Kerr - Property-Based Testing for Better Code • [MacIver 2016] David R. MacIver What is Property Based Testing? • [Steinhauser 2018] Jason Steinhauser - Intro to Property-Based Testing • [Hughes 2019] John Hughes - How to specify it youtu.be/zvRAyq5wj38
  26. Mathematische Gesetzte [Steinhauser 2018] Associative a + (b + c)

    = (a + b) + c a.append(b.append(c)) = a.append(b).append(c) Commutative a + b = b + a image.flipX().flipY() = image.flipY().flipX() Distributive a(b + c) = ab + ac a.ToUpper() + b.ToUpper() = (a + b).ToUpper() Idempotent f(a) = f(f(a)) list.Sort() = list.Sort().Sort() Identity f(a, i) where i is identity value of f = a list.appendAll([]) = list Zero f(a, z) where z is zero value of f = z intersect(valueSet, emptySet) = emptySet
  27. Statefull Testing • In Abstrakter-Phase wird eine gültige Kommando Sequenz

    erzeugt • In der Validierungs-Phase werden Kommandos gleichzeitig gegen das System und ein Modell ausgeführt. Jeder Output des Systems wird mittels des Modells überprüft. Fred Hebert 2019: Property-Based Testing with PropEr, Erlang, and Elixir
  28. • 4 bugs: micronaut(#442) h2(#2472), oracle spezial verhalten, zeitumstellung •

    Volvo: QuviQ John Hughes and his team analyzed 3k pages of requirements, wrote 20k lines of QuickCheck tests, and applied those properties to 1MM+ lines of vendor code. By checking their properties generated around the specifications, they found 200 issues that had slipped through the test fixtures that the vendors had generated... and 100 of those issues were in the actual specifications themselves. • Spotify: • Turn on playlist repeat. • Prepare playback of a playlist with a single track, let’s call this track A. • Play the prepared playlist. • Add a single track to the start of the playlist. • Skip to the next track. • IntelliJ: 285 issues