Gestaltung von Unit-Tests
Im Folgenden sind einige Konventionen für das Schreiben von Unit-Tests zusammengestellt. Es gibt viele verschiedene Meinungen und Ansätze an Unit-Tests heranzugehen; die folgenden Regeln haben sich in der Praxis als eine einfache Variante bewährt um schnell vorwärts zu kommen.
Wichtig ist, daß man sich für ein Vorgehen entscheidet und das dann möglichst konsequent durchzieht, am Besten in Absprache mit dem Entwicklungs-Team mit dem man gerade arbeitet.
Merke: Der Unit-Test soll den Entwickler unterstützen und jedem helfen der sie liest.
Regeln für Test-Klassen und Test-Methoden
Für die Beispiele betrachten wir eine einfache Klasse mit einer Methode
class TemperaturRechner { public long fahrenheit2Celsius(long f) { return (5 * f - 160) / 9; } }
- Für die zu testende Klasse wird eine eigene Test-Klasse mit dem Zusatz Test erstellt,
- Zur Klasse
TemperaturRechner
erstellt man eine KlasseTemperaturRechnerTest
- Alle Test-Methoden haben die gleiche Form (Kein Prä- oder Suffix "Test" am Methodennamen)
@Test public void xx(){...}
- Der Bezeichner der Test-Methoden soll in einem Satz beschreiben was getestet wird und was das erwartete Ergebnis ist.
- Getestet wird das Interface der zu testenden Klasse:
- Alle Methoden mit Ausnahme der private-Methoden werden getestet.
- Wenn zu viele Tests für eine Klasse zusammenkommen, kann man die Tests auch auf mehrere Testklassen verteilen. Schreibt man etwa für die Methode der obigen Klasse eine eigene Test-Klasse könnte si
TemperaturRechnerFahrenheit2Cslsius
heißen.
Nach diesen Regeln wird ein Unit-Test erstellt, der prüft, ob 32°F tatsächlich auf 0°C umgerechnet werden. Da der Methodenname nicht mit einer Ziffer beginnen kann, steht dabei die Einheit vor dem Wert. Alternativ kann man die Zahlen auch ausschreiben oder ein Wort voranstellen.
@Test public void fahrenheit32ErgibtCelsius0() { TemperaturRechner rechner = new TemperaturRechner(); long celsius = rechner.fahrenheit2Celsius(32); Assert.assertEquals(0, celsius); }
Wie immer ist beim Finden passender Namen Kreativität gefragt.
Aufbau eines Unit-Tests
Ein Unit-Test -- also einer einzelnen Testmethode -- hat in der Regel drei Schritte:
Test vorbereiten
In diesem Schritt werden Voraussetzungen für den Test geschaffen, zum Beispiel Test-Objekte oder -Daten erzeugt. in der Literatur wird der Versuchsaufbau auch als Fixture oder Setup bezeichnet. Hier werden ggf. auch Voraussetzungen dokumentiert. Wenn zum Beispiel ein nicht-leeres Objekt benötigt wird, kann man das mit einer Assumption sicherstellen.
Test durchführen
In diesem Schritt wird die zu testende Methode in "geeigneter Weise" aufgerufen.
Ergebnis prüfen
Am Ende des Unit-Tests muß immer eine Prüfung stehen; ohne Assertion ist der Test kein Test! Mit einer geeigneten Assertion prüft man, ob das gewünschte Ergebnis eingetreten ist.
Variationen
Test-Methoden komprimieren
Die drei – im vorangegangenen Abschnitt aufgeführten – Schritte müssen nicht explizit von einander getrennt sein. Kürzere Methoden sind oft leichter zu lesen als lange, erlaubt ist alles, was jeder andere Entwickler ohne Verrenkung verstehen kann. Den Beispiel-Test kann man auch so zusammendampfen:
@Test public void fahrenheit32ErgibtCelsius0() { Assertions.assertThat(new TemperaturRechner().fahrenheit2Celsius(32)).isEqualTo(0); }
Gemeinsame Test-Objekte
Die meisten Test-Klassen konzentrieren sich auf eine einzelne zu testende Klasse und benötigen oft nur eine einzige Instanz davon. Man kann sie daher als Feld der Test-Klasse deklarieren, initialisieren und in jedem Test verwenden. Das spart jedesmal eine Deklaration.
Als Bezeichner kann man z.B. testInstanz
oder testObjekt
verwenden, aber auch einen
zur Klasse passenden Begriff wie im Code-Beispiel unten.
Erlaubt ist alles, was leserlich ist, je leichter umso besser. Abkürzungen sollten immer dann vermieden werden, wenn sie für die Leserschaft nicht selbstverstädlich ist. "sut" anstelle von "subjectUnderTest" führt eher zur Verwirrung.
Der Beispiel-Test sieht dann so aus:
public void TemperaturRechnerTest { TemperaturRechner rechner = new TemperaturRechner(); @Test public void fahrenheit32ErgibtCelsius0() { Assertions.assertThat(rechner.farenheit2Celsius(32)).isEqualTo(0); } }