Wenn die Realität stört: Warum brauchen wir Mocks und Fixtures in Go?
\n\nStellen Sie sich vor, Sie testen eine Funktion, die eine E-Mail-Benachrichtigung sendet. Jeder Testlauf ist eine echte E-Mail im Posteingang. Nach zehn Tests ist Ihr Postfach voll und Ihre Kollegen verfluchen den Spam. Oder schlimmer: Die Funktion greift auf eine externe API zu, die kostenpflichtig ist oder nur einmal am Tag läuft.
\n\nIn der Welt von Go (oder Golang, wie es oft genannt wird) ist Testen nicht nur ein Häkchen auf der Checkliste. Es ist das Fundament der Zuverlässigkeit. Damit Tests jedoch schnell und unabhängig sind, müssen wir den Code von der Außenwelt isolieren. Genau hier kommen Mock-Objekte (Mocks) und Fixtures ins Spiel.
\n\nIn diesem Artikel werden wir uns ansehen, wie man Abhängigkeiten richtig ersetzt (mockt) und Daten (Fixtures) in Go vorbereitet. Sie erfahren, wie Sie Tests schreiben, die nicht aufgrund von Netzwerk, Datenbank oder Tageszeit fehlschlagen. Los geht's!
\n\nWas sind Mock und Fixture im Kontext von Go?
\n\nMock-Objekte: Stellvertreter für komplexe Systeme
\n\nMock-Objekte sind programmatische "Platzhalter", die das Verhalten realer Komponenten nachahmen. Anstatt eine echte Datenbank oder einen HTTP-Dienst aufzurufen, setzen wir ein Objekt ein, das vordefinierte Antworten zurückgibt.
\n\nIn Go sind Mocks besonders wichtig, da die Sprache die Arbeit mit Interfaces fördert. Wenn Ihr Code von einem Interface abhängt, können Sie die Implementierung leicht durch eine Testimplementierung ersetzen.
\n\nFixtures: Die Bühne bereiten
\n\nFixtures sind Daten oder Zustände, die vor Beginn eines Tests eingerichtet werden müssen. Zum Beispiel das Erstellen einer temporären Datenbank, das Befüllen mit Testdatensätzen oder das Konfigurieren des Dateisystems.
\n\nIm Gegensatz zu Mocks, die das Verhalten ersetzen, bereiten Fixtures die Umgebung vor. Sie beantworten die Frage: "Was muss bereit sein, damit der Test starten kann?"
\n\nMocks in Go schreiben: Vom Einfachen zum Komplexen
\n\nDer einfachste Weg: Manuelle Implementierung eines Interfaces
\n\nGo erfordert keine speziellen Bibliotheken zum Erstellen von Mocks. Wenn Sie ein Interface haben, können Sie dessen Testimplementierung manuell schreiben. Dies ist ideal für kleine Projekte.
\n\nAngenommen, wir haben ein Interface zum Senden von Benachrichtigungen:
\n\npackage notifier\n\ntype Notifier interface {\n Send(to string, message string) error\n}\n\n// Echte Implementierung (nicht für Tests)\ntype EmailNotifier struct{}\n\nfunc (e *EmailNotifier) Send(to string, message string) error {\n // Echte E-Mail senden\n return nil\n}\n\nIn der Testdatei erstellen wir einen Mock:
\n\npackage notifier\n\nimport "testing"\n\ntype MockNotifier struct {\n Called bool\n LastTo string\n LastMsg string\n}\n\nfunc (m *MockNotifier) Send(to string, msg string) error {\n m.Called = true\n m.LastTo = to\n m.LastMsg = msg\n return nil\n}\n\nfunc TestSendAlert(t *testing.T) {\n mock := &MockNotifier{}\n // Verwenden des Mocks in einer Funktion, die Notifier akzeptiert\n SendAlert(mock, "user@test.com", "Hello!")\n \n if !mock.Called {\n t.Error("Methode Send wurde nicht aufgerufen")\n }\n if mock.LastTo != "user@test.com" {\n t.Errorf("Falscher Empfänger: %s", mock.LastTo)\n }\n}\n\nVorteile: Volle Kontrolle, keine Abhängigkeiten.
\nNachteile: Viel Boilerplate-Code für komplexe Interfaces.
Verwendung von testify/mock: Leistungsstark und bequem
\n\nDie Bibliothek testify ist der De-facto-Standard für Mocks in Go. Sie bietet ein flexibles Framework zum Erstellen von Mocks mit Überprüfung von Argumenten und Aufrufanzahlen.
Installation:
\ngo get github.com/stretchr/testify\n\nBeispiel mit testify/mock:
\n\npackage user\n\nimport (\n "testing"\n "github.com/stretchr/testify/mock"\n)\n\n// Interface definieren\ntype UserRepository interface {\n GetUser(id int) (string, error)\n}\n\n// Mock-Struktur erstellen\ntype