Wie führt man alle Tests in einem Go-Projekt aus und was passiert "unter der Haube"?
\nJeder Go-Entwickler hat mindestens einmal in seinem Leben den magischen Befehl go test ./... im Terminal eingegeben und zugesehen, wie die grünen Häkchen über den Bildschirm laufen. Aber haben Sie sich jemals gefragt, was in diesem Moment wirklich passiert? Warum werden Tests genau so und nicht anders ausgeführt? Und warum überspringt go test manchmal stillschweigend Ihre brandneuen Tests?
In diesem Artikel beantworten wir nicht nur die beliebte Frage von Stack Overflow (wo sie tausende Aufrufe gesammelt hat), sondern werfen einen Blick unter die Haube des Go-Compilers und erfahren, wie der Testmechanismus aufgebaut ist. Los geht's!
\n\nWarum ist go test ./... Magie?
\nBeginnen wir mit den Grundlagen. Der Befehl go test ohne Argumente führt Tests nur im aktuellen Paket aus. Aber wenn Sie ein Projekt mit Dutzenden von Paketen haben (und das ist in der realen Produktion der Fall), ist das manuelle Durchgehen eine Art mittelalterliche Folter. Hier kommt ./... zur Hilfe.
Dieses Muster bedeutet: "Finde alle Pakete rekursiv, beginnend mit dem aktuellen Verzeichnis". Go durchläuft den Verzeichnisbaum, ignoriert vendor und Module, und sammelt eine Liste aller Pakete. Für jedes davon wird eine separate Instanz von go test gestartet.
Aber was, wenn Sie Tests nur in bestimmten Verzeichnissen ausführen möchten? Zum Beispiel nur in ./internal/...? Es funktioniert genauso: go test ./internal/....
Wichtig: Wenn Sie Go Modules verwenden (und ab Go 1.16+ ist das der Standard), funktioniert ./... nur innerhalb des aktuellen Moduls. Um alle Module im Workspace zu testen, müssen Sie go test ./... in jedem einzelnen ausführen oder eine go.work-Datei konfigurieren.
Und was ist ein "Paket" im Kontext von go test?
\nGo arbeitet mit dem Konzept des Pakets – einer Gruppe von .go-Dateien in einem Verzeichnis. Testdateien (mit dem Suffix _test.go) werden ebenfalls als Teil des Pakets betrachtet, aber separat kompiliert. Wenn Sie go test ausführen, kompiliert Go eine Test-Binärdatei, die Folgendes enthält:
- \n
- Den Quellcode des Pakets (aber nur das, was in den Tests verwendet wird) \n
- Den Code der Tests selbst \n
- Eine spezielle Laufzeit-Umgebung für die Ausführung und Berichterstattung \n
Diese Binärdatei wird ausgeführt, führt die Tests durch und gibt das Ergebnis aus. Der gesamte Prozess ist für jedes Paket separat.
\n\nWie entscheidet Go, welche Tests ausgeführt werden?
\nWenn Sie go test ./... eingeben, führt Go die folgenden Schritte aus:
- \n
- Durchlaufen des Dateisystems – Go findet alle Verzeichnisse, die
.go-Dateien enthalten. \n - Filterung – Verzeichnisse wie
vendor,testdataund versteckte Ordner (die mit.beginnen) werden ignoriert. \n - Kompilierung – Für jedes Paket wird eine temporäre Binärdatei erstellt. \n
- Ausführung – Die Binärdatei wird ausgeführt, die Ergebnisse werden gesammelt. \n
- Caching – Wenn sich der Code nicht geändert hat, verwendet Go den Cache und zeigt
(cached)an. \n
Der letzte Punkt ist eine häufige Ursache für Überraschungen. Sie haben einen Test geändert, führen go test ./... aus, und es sagt "ok (cached)". Warum? Weil Go die Ergebnisse auf Basis eines Hashs der Eingabedateien zwischenspeichert. Wenn Sie nur einen Kommentar oder einen ungenutzten Import geändert haben, ändert sich der Hash nicht, und der Test wird nicht neu gestartet.
Lifehack: Sie können den Cache mit dem Befehl go clean -testcache erzwingen zurücksetzen. Oder verwenden Sie das Flag -count=1, das Go anweist, den Cache zu ignorieren: go test -count=1 ./....
Parallelität: Warum sind Tests nicht immer schneller?
\nStandardmäßig führt go test Tests innerhalb eines Pakets sequentiell aus. Aber zwischen den Paketen – parallel! Die Anzahl der gleichzeitigen Pakete wird durch das Flag -p bestimmt (standardmäßig gleich der Anzahl der CPUs).
Das klingt großartig, aber es gibt einen Haken: E