Property-based
testing

Frank S. Thomas

Inhalt

  1. Was ist Property-based testing?
  2. Code-Beispiele
  3. Property-Design

Rahmen

Teststufen

  • Unittest
  • Integrationstest
  • Systemtest
  • Akzeptanztest

Testen mit Properties ist nicht auf Unittests beschränkt!

Testen mit Beispielen

Vergleich von Eingaben mit erwarteten Ausgaben


            assert("Hallo Welt".split(" ") == Array("Hallo", "Welt"))
          

Testen mit Beispielen: Vorteile

  • einfach zu schreiben und zu lesen
  • einfach zu erweitern
  • Dokumentation für andere Entwickler

Testen mit Beispielen: Nachteile

  • einfach Randfälle zu übersehen
  • Testabdeckung abhängig von Anzahl der Beispiele

Was ist property-based testing?

Property

Eigenschaft einer Komponente, die unter spezifizierten Bedingungen für alle Eingaben zutrifft

Beispiel

Die Addition der reellen Zahlen ist kommutativ


            for all a, b: a + b == b + a
          

Beispiel

Beim Zusammenfügen von zwei Listen ist die Länge der neuen Liste die Summe der Längen der Ausgangslisten


            for all l1, l2: l1.append(l2).length == l1.length + l2.length
          

Beispiel

java.lang.Comparable

Beispiel

java.lang.Comparable

Was ist property-based testing?

  • Entwickler schreibt Property
  • Test-Framework generiert viele zufällige Testdaten
  • ... und validiert Property mit diesen Testdaten

l1 = [],    l2 = [] : l1.append(l2).length == l1.length + l2.length -> true
l1 = [0],   l2 = [] : l1.append(l2).length == l1.length + l2.length -> true
l1 = [],    l2 = [1]: l1.append(l2).length == l1.length + l2.length -> true
l1 = [1,2], l2 = [] : l1.append(l2).length == l1.length + l2.length -> true
...
          

Demo!

Property-Design

unvollständige Properties

  • mit einfachen Fakten beginnen
  • Properties legen einen Rahmen um die Implementierung
  • vollständige Spezifikation kann Code Duplizierung bedeuten

relationale Properties


forAll {
  (x: T, y: T) => (x.compareTo(y) == 0) == x.equals(y)
}
          

Referenzimplementierung

Methoden einer neuen Klasse MyMap sollen sich verhalten wie die Methoden von HashMap


forAll {
  (h: HashMap) =>
    val m = MyMap.fromHashMap(h)
    m.foo() == h.foo()
}
          

Round-trip Properties

Wenn Umkehrfunktion vorhanden: x == g(f(x))


forAll {
  (s: String) => s == utf8Decode(utf8Encode(s))
}
          

Testen mit Properties

  • Spezifikation von Eigenschaften, die für alle Eingaben zutreffen
  • Trennung der Tests und Generierung der Testdaten
  • (teilweise) schwierig zu schreiben
  • Randfälle müssen nicht extra betrachtet werden
  • ein Property deckt eine Vielzahl von Beispielen ab
    => größere Testabdeckung durch weniger Code

The End