Testen mit Stubs (in Java)

Eine einfache Methode um Testwerte anzunehmen nennt sich Stubs. Dabei werden Methoden – welche noch nicht existieren, auf Webservices oder Datenbankzugriff angewiesen sind – gefakt. Diese erleichtern das Testen ungemein und helfen unabhängiger und noch granularer zu testen.

JUnit Java Stubs
JUnit Stubs @Java

Eine immer wiederkehrende Problematik in der Programmierung ist die Verfügbarkeit von externen Diensten. Ich möchte eine Software entwickeln und bin dabei jedoch auf Netzwerkserver, Webservices oder Datenbanken angewiesen. Was wenn diese momentan nicht verfügbar sind, da ich von zuhause arbeite oder offline?

Ein anderes Szenario ist, dass ich generelle Funktionen teste welche ebenfalls Abfragen oder Inserts auf die Datenbank enthalten. Ich möchte nicht immer wieder Testdaten in die Datenbank schreiben und diese anschließend löschen. Weiter denkbar sind Webservices, bei denen die Anfragezeit recht hoch ist und das Testen sich dadurch ewig ziehen würde.

Die Lösung: Stubs!

Hier gibt es ein Zauberwort: Stubs (Stummel). Diese können abhängige Methoden und deren Rückgabewerte überschreiben und somit Testwerte zurückgeben, welche die eigentliche Funktion erstmal ausklammern. Dazu möchte ich hier ein recht banales Beispiel zeigen, welches jedoch die Thematik ganz gut veranschaulicht.

Stub Beispiel: Step by Step

Ich erstelle ein kleines Tool, welches ein Eurobetrag in Dollar, Schweizerfranken oder wieder in Euro konvertiert. Der Haken dabei ist, dass der Wechselkurs jeweils von Yahoo ermittelt wird! … Keine Angst, hier wird nicht erläutert wie der Webservice implementiert wird. Im Gegenteil, ich übergehe den Webservice jeweils mit einem Stub.

Hauptklasse MoneyChange

Fangen wir mit der ersten Klasse an. Die Hauptklasse, welche die eigentlichen Methoden enthält „MoneyChangeEuro.java“. „getYahooCurrencyRate()“ spricht dabei jeweils den Yahoo-Webservice an. Der Witz bei der Sache ist, die Methode gibt es schlicht und einfach nicht in meinem Programm. Das heißt, diese müssen mit einem Stub ersetzt werden um Werte anzunehmen.

public class MoneychangeEuro 
{ //Wechselt immer von Euro in ...
 
    public double toEuro(double euroNotation)
    {//gibt eins zu eins zurück
        return euroNotation;
    }
 
    public double toDollar(double euroNotation)
    {//gibt Euro in Dollar zurück
        //holt den Wechselkurs vom YahooWebService
        double currencyRate = getYahooCurrencyRate('EUR>DOLLAR');
        return euroNotation * currencyRate;
    }
     
    public double toSFR(double euroNotation)
    {//gibt Euro in SFR zurück
        //holt den Wechselkurs vom YahooWebService
        double currencyRate = getYahooCurrencyRate('EUR>SFR');
        return euroNotation * currencyRate;
    }
}

Wer jetzt meint „das ist ja total abwegig, dass es die Methode nicht gibt“ den muss ich eines besseren belehren. Die Situation, dass gewissen Teile eines Programms noch nicht existieren, jedoch trotzdem in aktuelle Methoden bereits mit einfließen, spiegelt die Realität wieder. Stell dir einfach vor, dass ich die Umrechnung entwickeln soll und eine zweite Abteilung – die zeitlich im Rückstand ist – die Ansteuerung zu Yahoo umsetzen soll. Getreu dem Motto „Time is Money“ kann ich nicht auf die anderen Entwickler warten, ich muss fertig werden.

Hier bleibt mir nur eins – Werte annehmen = Stubs!

Stub-Klasse

Dazu ziehen wir eine neue Klasse auf, die von unserer ursprünglichen und fehlerhaften, da die getYahooCurrencyRate-Methode nicht existiert, Klasse erbt. Somit ist in dieser vorerst genau der gleiche Code enthalten wie in der Ursprungsklasse. Jedoch überschreiben wir toDollar() und toSFR() mit Stubs.

public class MoneychangeEuroStub extends MoneychangeEuro 
{
    @Override
    public double toDollar(double euroNotation) {
        return euroNotation * 1.30;
    }
     
    @Override
    public double toSFR(double euroNotation) {
        return euroNotation * 1.20;
    }
}

Dabei lassen wir die Methode toEuro() unberührt, da diese auf keinen Webservice angewiesen ist. Instanziiere ich jetzt ein Objekt von MoneychangeEuroStub, kann ich genauso auf die drei Methoden zugreifen, ohne dass ich Gefahr laufe nur Fehlermeldungen zu produzieren.

Test-Klasse

Zum guten Schluss schreiben wir noch die JUnit-Testklasse. Dabei kann man gut sehen, dass ich für den Test die MoneychangeEuroStub-Klasse heranziehe. Dieser Test kann ohne Schwierigkeiten ausgeführt werden.
[code language=“java“]
import static org.junit.Assert.*;
import org.junit.Test;

public class TestMoneychangeEuro {
final double DELTA = 0.0001; //Abweichung (nur für Vergleich)

import static org.junit.Assert.*;
import org.junit.Test;
 
public class TestMoneychangeEuro {
    final double DELTA = 0.0001; //Abweichung (nur für Vergleich)
     
    @Test
    public void test() 
    {
        double euroValue = 3.0;
        MoneychangeEuro mcE = new MoneychangeEuroStub();
         
        assertEquals("Wechsel zu Euro stimmt nicht", euroValue, mcE.toEuro(euroValue),DELTA);
        assertEquals("Wechsel zu Dollar stimmt nicht", 3.9, mcE.toDollar(euroValue),DELTA);
        assertEquals("Wechsel zu SFR stimmt nicht", 3.6, mcE.toSFR(euroValue),DELTA);   
    }
}

Wenn jetzt die zweite Abteilung mit dem Webservice fertig ist, kann die Zeile 11 einfach für die originale Klasse ausgetauscht werden.

MoneychangeEuro mcE = new MoneychangeEuro();

Download

MrKnowing

Programmierer und Wissensnerd! Kontaktiere mich auf Google+ oder einfach per Mail danny@mrknowing.com

Das könnte dich auch interessieren …

Eine Antwort

  1. 24. Mai 2013

    […] Testfällen nachzukommen und Abhängigkeiten zu entkoppeln, hat man sich früher mit sogenannten Stubs rumgeschlagen. Für jede Abhängigkeit wurde eine separate, eigens dafür angelegte, Klasse […]

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert