© PowerUp/Shutterstock.com
Java Magazin
Effektive Unit-Tests - Beispiele und Properties

Property-based Testing mit Java

Testgetriebene Entwicklung in objektorientierten Sprachen setzt bislang meist auf beispielbasierte Testfälle, wie sie sich einfach mit JUnit und ähnlichen Testframeworks erstellen lassen. Schauen wir jedoch über den Tellerrand und auf funktionale Programmiersprachen wie Haskell oder F#, finden wir dort etwas anderes: Property-Tests.

Johannes Link


Property-Tests basieren auf der Idee, die erwünschten Properties unseres Programms zu beschreiben und anschließend das Framework selbstständig Testfälle generieren zu lassen, die diese Eigenschaften überprüfen und möglicherweise falsifizieren. Auch in Java ist das möglich und sinnvoll.

Beginnen wir die Reise ins Land der Properties mit einem einfachen JUnit-5-Jupiter-Test [1] für eine gängige JDK-Funktion: java.util.Collections.reverse() dient dazu, die Reihenfolge aller Elemente einer Liste umzukehren. Ein typischer Test wird diese Funktionalität anhand eines konkreten Beispiels überprüfen – in unserem Fall einer Liste mit drei Elementen (Listing 1).

Listing 1: Beispieltest für „Collections.reverse()“import java.util.*;import org.assertj.core.api.Assertions;import org.junit.jupiter.api.Test; class CollectionsTests { @Test void reverseList() { List aList = Arrays.asList(1, 2, 3); Collections.reverse(aList); Assertions.assertThat(aList).containsExactly(3, 2, 1); }}

Mit solchen Tests arbeiten (hoffentlich) die meisten Entwickler seit Jahren. Doch ein Gedanke treibt mich dabei immer wieder um: Woher weiß ich, dass reverse auch mit fünf Elementen funktioniert? Und mit 5 000? Und mit der leeren Menge? Und mit Stringelementen? Und, und, und ... Diese Unsicherheit bekämpfe ich typischerweise, indem ich zusätzliche Beispieltests hinzufüge und hoffe, dass meine ausgesuchten Beispiele ausreichend repräsentativ sind, um jetzt und in Zukunft mögliche Implementierungsfehler zu entdecken und Regressionen zu vermeiden. Modelbasierte Testansätze wie Äquivalenzklassenbildung versuchen genau dieses Problem abzuschwächen.

Was ist eine Property?

Tatsächlich können wir uns der zu validierenden Funktionalität auch von einer anderen Seite nähern, nämlich mit der Fragestellung: Unter welchen Vorbedingungen (z. B. erlaubte Eingangsparameter) müssen welche allgemeinen Eigenschaften (Invarianten und Nachbedingungen) erfüllt sein? Dieses Gespann aus Vorbedingungen plus allgemeinen Eigenschaften heißt, insbesondere im Bereich der funktionalen Programmierung, auch Property.

Formulieren wir zur Veranschaulichung eine Property für die reverse-Funktion in Worten: „Für jede eingehende Liste ergibt die zweifache Anwendung von reverse wieder die ursprüngliche Liste.“ Formulieren wir diese Property nun in einer Form, die der Computer interpretieren kann, etwa als Programmcode, und überlassen dann die Generierung von Beispielen, die den Vorbedingungen entsprechen, eben dies...

Java Magazin
Effektive Unit-Tests - Beispiele und Properties

Property-based Testing mit Java

Testgetriebene Entwicklung in objektorientierten Sprachen setzt bislang meist auf beispielbasierte Testfälle, wie sie sich einfach mit JUnit und ähnlichen Testframeworks erstellen lassen. Schauen wir jedoch über den Tellerrand und auf funktionale Programmiersprachen wie Haskell oder F#, finden wir dort etwas anderes: Property-Tests.

Johannes Link


Property-Tests basieren auf der Idee, die erwünschten Properties unseres Programms zu beschreiben und anschließend das Framework selbstständig Testfälle generieren zu lassen, die diese Eigenschaften überprüfen und möglicherweise falsifizieren. Auch in Java ist das möglich und sinnvoll.

Beginnen wir die Reise ins Land der Properties mit einem einfachen JUnit-5-Jupiter-Test [1] für eine gängige JDK-Funktion: java.util.Collections.reverse() dient dazu, die Reihenfolge aller Elemente einer Liste umzukehren. Ein typischer Test wird diese Funktionalität anhand eines konkreten Beispiels überprüfen – in unserem Fall einer Liste mit drei Elementen (Listing 1).

Listing 1: Beispieltest für „Collections.reverse()“import java.util.*;import org.assertj.core.api.Assertions;import org.junit.jupiter.api.Test; class CollectionsTests { @Test void reverseList() { List aList = Arrays.asList(1, 2, 3); Collections.reverse(aList); Assertions.assertThat(aList).containsExactly(3, 2, 1); }}

Mit solchen Tests arbeiten (hoffentlich) die meisten Entwickler seit Jahren. Doch ein Gedanke treibt mich dabei immer wieder um: Woher weiß ich, dass reverse auch mit fünf Elementen funktioniert? Und mit 5 000? Und mit der leeren Menge? Und mit Stringelementen? Und, und, und ... Diese Unsicherheit bekämpfe ich typischerweise, indem ich zusätzliche Beispieltests hinzufüge und hoffe, dass meine ausgesuchten Beispiele ausreichend repräsentativ sind, um jetzt und in Zukunft mögliche Implementierungsfehler zu entdecken und Regressionen zu vermeiden. Modelbasierte Testansätze wie Äquivalenzklassenbildung versuchen genau dieses Problem abzuschwächen.

Was ist eine Property?

Tatsächlich können wir uns der zu validierenden Funktionalität auch von einer anderen Seite nähern, nämlich mit der Fragestellung: Unter welchen Vorbedingungen (z. B. erlaubte Eingangsparameter) müssen welche allgemeinen Eigenschaften (Invarianten und Nachbedingungen) erfüllt sein? Dieses Gespann aus Vorbedingungen plus allgemeinen Eigenschaften heißt, insbesondere im Bereich der funktionalen Programmierung, auch Property.

Formulieren wir zur Veranschaulichung eine Property für die reverse-Funktion in Worten: „Für jede eingehende Liste ergibt die zweifache Anwendung von reverse wieder die ursprüngliche Liste.“ Formulieren wir diese Property nun in einer Form, die der Computer interpretieren kann, etwa als Programmcode, und überlassen dann die Generierung von Beispielen, die den Vorbedingungen entsprechen, eben dies...

Neugierig geworden?


    
Loading...

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang