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
pak
deklariere ich eine VariableMach
vom Typ der Funktionimp.Mach
—Mach
ist 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
, sondernMach
auf:// 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 Paketpak
genausogut wie vorher. -
Im Testkode deklariere ich eine Dummy-Funktion
dumMach
mit derselben Signatur, die auchimp.Mach
hat. Zu Beginn der Testfunktion weise ich der VariablenMach
die Dummy-Funktion zu; dadurch wird vonfunk
jetzt diese anstelle der originalen Funktionimp.Mach
benutzt:// 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 Funktionfunk
ohne dass von dort Kode aus dem Paketimp
aufgerufen 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