Die Nutzung dieses Werks ist unter den Bedingungen der "Creative Commons Namensnennung - Keine Bearbeitungen 4.0 International"-Lizenz erlaubt.
Testen mit Funktionszeigern
(Update: 24.11.2014)
Dieser Aufsatz zeigt, dass auch Funktionszeiger das Testen leichter machen können.
Im letzten Aufsatz, das war "Testen mit Interfaces", habe ich beschrieben, wie man Methoden aus fremden Paketen durch Dummies ersetzen kann. Das ist hilfreich, weil man dadurch das Testen auf den Kode des zu testenden Pakets einschränkt. Der Beitrag von Matt Ho in diesem Gesprächsfaden hat mir gezeigt, dass man Gleiches auch für Funktionen aus fremden Paketen erreichen kann.
Starten wir also ähnlich wie beim letzten Mal:
// funktion.go enthält die Funktion funk.
package pak
import "pfad/zu/imp"
func funk() {
//...
imp.Mach()
//...
}
// funktion_test.go enthält einen Test für die Funktion funk.
package pak
import "testing"
func TestFunk(t *testing.T) {
//...
funk()
//...
}
Die Funktion funk im Paket pak ruft also die Funktion
Mach aus dem (eventuell fremden) Paket imp:
package imp
func Mach() { ... }
Problem und Lösung
Wenn ich nun die Testroutine TestFunk starte, um die Funktion
funk zu testen, so möchte ich das tun ohne mittelbar auch
die fremde Funktion imp.Mach zu testen.
In Go sind Funktionen "Bürger erster Klasse"; man kann vieles mit ihnen tun. Man kann sie Variablen zuweisen, man kann sie Funktionen als Parameter mitgeben und natürlich können Funktionen auch Funktionen als Ergebniswerte zurückgeben. Das Erstere wollen wir nutzen:
-
Im Paket
pakdeklariere ich eine VariableMachvom Typ der Funktionimp.Mach—Machist ein Funktionszeiger. Bevor der benutzt werden kann, muss er versorgt werden, am besten in einerinit-Funktion. Und dann rufe ich infunk(oder wo auch immer) nicht mehrimp.Mach, sondernMachauf:// funktion.go enthält die Funktion funk. package pak import "pfad/zu/imp" var Mach func() func init() { Mach = imp.Mach } func funk() { // ... Mach() // ... }Damit funktioniert das Paketpakgenausogut wie vorher. -
Im Testkode deklariere ich eine Dummy-Funktion
dumMachmit derselben Signatur, die auchimp.Machhat. Zu Beginn der Testfunktion weise ich der VariablenMachdie Dummy-Funktion zu; dadurch wird vonfunkjetzt diese anstelle der originalen Funktionimp.Machbenutzt:// funktion_test.go enthält einen Test für die Funktion funk. package pak import "testing" func dumMach() {} func TestFunk(t *testing.T) { Mach = dumMach // ... funk(v) // ... }Also testet TestFunk jetzt die Funktionfunkohne dass von dort Kode aus dem Paketimpaufgerufen werden müsste ... Applaus!
Kompliziertere Fremdfunktionen
Mach hatte bisher eine Signatur, wie sie einfacher nicht geht, nämlich
eine ohne Parameter und ohne Ergebnisparameter. Aber anders funktioniert's genauso.
Nehmen wir an, Mach sähe so aus:
func Mach(in imp.Struk1) (out imp.Struk2, err error) { ... }
Dann müssen in funktion.go der Funktionszeiger so:
var Mach func(imp.Struk1) (imp.Struk2, error)
und in funktion_test.go die Dummy-Funktion so:
import "pfad/zu/imp"
func dumMach(in imp.Struk1) (out imp.Struk2, err error) { return }
aussehen. Wegen der extern deklarierten Strukturen muss für den Test
jetzt auch imp importiert werden.
Zur Laufzeit des Tests wird beim Aufruf von Mach, also
dumMach, lediglich etwas nach in übergeben, und die
Variablen out und err werden initialisiert;
mehr passiert hier aber auch nicht.
Noch mehr Info
Allgemein zum Testen in Go möchte ich wieder verweisen auf " Wie man mit Go arbeitet" und auf die Doku zum Paket testing.
(Update: 24.11.2014)
Für Leser, die des Englischen mächtig sind, hier noch der Verweis auf einen Vortrag
von Andrew Gerrand:
Testing Techniques,
und auf die
dazugehörenden Dias.
Ein schönes Beispiel dafür, was man mit Funktionszeigern in Go noch machen
kann, ist die Funktion makeHandler in diesem Aufsatz:
"Netzdienste schreiben".
November 2014
