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.
4. 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
5. 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
9. PBT Orakel
[Kerr 2014]
Input
Output
Automatische Software Tests ○ Property Based Testing ○ Beispiel ○ Frameworks ○ Properties ○ Fazit
Generierte Inputs + Überprüfung der Eigenschaften der Outputs
10. 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
15. 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
16. 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
17. 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
18. 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
20. 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
21. 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
23. 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
24. 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
26. 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
27. 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
28. 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
30. 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
31. 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
32. 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
34. 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
35. 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
36. • 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
Hinweis der Redaktion
Freu das ihr eingeschalten habt
Geht um properties testen
Was properties sind und welches es gibt
Freue ich mich schon auf eure fragen
Alle edge cases testen + code coverage
Was hat das mit automatischen tests zu tun
Problem, weil sonst kein talk
Theories: für diese 5 primzahlen finde ich keinen kleinsten gemeinsamen teiler anders als 1 oder n
Combi: wichtige inputs auswählen aus paarweisen combinationen
Viele inputs probieren und dann programatisch bestimmen ob die ergebnisse stimmen
Oft zu scchwierig, daher her
Warum härter dran abend um weniger spezifisch zu sein?
Weniger spezifischer output, mehr freiheit in der implementierung (beispiel?)
PBT ist im vergleich zum reinen fuzzing spezifisch
Eine pbt lib bringt einen fuzzer + dsl um fachlichen properties zu beschreiben mit
Merge / quick ganz anders => whitebox info anders
Oft gestaffelte algorithmen (unterschiedlicher algorithmus für unterschiedlich listen größen)
Merge / quick ganz anders => whitebox info anders
Oft gestaffelte algorithmen (unterschiedlicher algorithmus für unterschiedlich listen größen)
Edh: leere liste zurückgeben
Keine gedanken um edge cases
Integrieren sich in gänge test frameworks: java -> junit
Nicht völlig random
Praktisches wissen in basis generatoren eingeflossen
Nicht völlig random
Praktisches wissen in basis generatoren eingeflossen
Equals generiere zwei strings, unwahrscheinlich das gleiche gegened werden
Größe von collections beschränken