Testen mit Stubs (in Java)

Share on FacebookShare on Google+Share on LinkedInTweet about this on TwitterShare on TumblrEmail this to someone

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.

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

Share on FacebookShare on Google+Share on LinkedInTweet about this on TwitterShare on TumblrEmail this to someone

MrKnowing

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

You may also like...

1 Response

  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 […]

Hinterlasse eine Antwort

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

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>