Gruß an alle!
Bis zum Beginn des
Kurses „Golang Developer“ verbleibt weniger als eine Woche, und wir teilen weiterhin nützliches Material zu diesem Thema. Lass uns gehen!

Go verfügt über eine gute und zuverlässige integrierte Bibliothek zum Testen. Wenn Sie auf Go schreiben, wissen Sie das bereits. In diesem Artikel werden wir über verschiedene Strategien sprechen, mit denen Sie Ihre Testfähigkeiten mit Go verbessern können. Durch die Erfahrung mit dem Schreiben unserer beeindruckenden Codebasis auf Go haben wir gelernt, dass diese Strategien wirklich funktionieren und somit Zeit und Mühe bei der Arbeit mit dem Code sparen.
Verwenden Sie TestsuitenWenn Sie aus diesem Artikel nur eine nützliche Sache für sich selbst lernen, muss es sich um die Verwendung von Testsuiten handeln. Für diejenigen, die mit diesem Konzept nicht vertraut sind, ist das Testen mit Kits der Prozess der Entwicklung eines Tests zum Testen einer gemeinsamen Schnittstelle, die für viele Implementierungen dieser Schnittstelle verwendet werden kann. Unten sehen Sie, wie wir verschiedene
Thinger
Implementierungen bestehen und mit denselben Tests ausführen.
type Thinger interface { DoThing(input string) (Result, error) }
Glückliche Leser haben mit Codebasen gearbeitet, die diese Methode verwenden. Häufig in Plugin-basierten Systemtests verwendet, die zum Testen einer Schnittstelle geschrieben wurden, können alle Implementierungen dieser Schnittstelle verwendet werden, um zu verstehen, wie sie die Verhaltensanforderungen erfüllen.
Die Verwendung dieses Ansatzes kann möglicherweise dazu beitragen, Stunden, Tage und sogar genug Zeit zu sparen, um das Problem der Gleichheit der
Klassen P und NP zu lösen. Wenn Sie ein Basissystem durch ein anderes ersetzen, müssen Sie nicht mehr (eine große Anzahl) zusätzlicher Tests schreiben, und es besteht auch die Gewissheit, dass dieser Ansatz den Betrieb Ihrer Anwendung nicht stört. Implizit müssen Sie eine Schnittstelle erstellen, die den Bereich des getesteten Bereichs definiert. Mithilfe der Abhängigkeitsinjektion können Sie einen Satz aus einem Paket anpassen, das an die Implementierung des gesamten Pakets übergeben wird.
Ein vollständiges Beispiel finden Sie
hier . Trotz der Tatsache, dass dieses Beispiel weit hergeholt ist, kann man sich vorstellen, dass eine Datenbank remote und die andere im Speicher ist.
Ein weiteres cooles Beispiel für diese Technik befindet sich in der Standardbibliothek im Paket
golang.org/x/net/nettest
. Es bietet eine Möglichkeit zu überprüfen, ob net.Conn die Schnittstelle erfüllt.
Kontamination der Schnittstelle vermeidenSie können nicht über das Testen in Go sprechen, aber nicht über Schnittstellen.
Schnittstellen sind im Zusammenhang mit Tests wichtig, da sie das leistungsstärkste Werkzeug in unserem Testarsenal sind. Sie müssen sie daher korrekt verwenden.
Pakete exportieren häufig Schnittstellen an Entwickler, und dies führt dazu, dass:
A) Entwickler erstellen ihr eigenes Modell, um das Paket zu implementieren.
B) Das Paket exportiert sein eigenes Modell.
"Je größer die Schnittstelle, desto schwächer die Abstraktion"
- Rob Pike, Sprüche von Go
Schnittstellen müssen vor dem Export sorgfältig geprüft werden. Es ist oft verlockend, Schnittstellen zu exportieren, um Benutzern die Möglichkeit zu geben, das von ihnen benötigte Verhalten zu simulieren. Dokumentieren Sie stattdessen, welche Schnittstellen zu Ihren Strukturen passen, um keine enge Beziehung zwischen dem Verbraucherpaket und Ihrem eigenen herzustellen. Ein gutes Beispiel dafür ist das
Fehlerpaket .
Wenn wir eine Schnittstelle haben, die wir nicht exportieren möchten, können wir den
internen Teilbaum / Paket-Teilbaum verwenden , um sie im Paket zu speichern. Wir können daher nicht befürchten, dass der Endbenutzer von ihm abhängig ist, und können daher die Benutzeroberfläche flexibel an neue Anforderungen anpassen. Normalerweise erstellen wir Schnittstellen mit externen Abhängigkeiten, um Tests lokal ausführen zu können.
Dieser Ansatz ermöglicht es dem Benutzer, seine eigenen kleinen Schnittstellen zu implementieren, indem er einfach einen Teil der Bibliothek zum Testen umschließt. Weitere Informationen zu diesem Konzept finden Sie im
Rakyl-Artikel zur Grenzflächenverschmutzung .
Exportieren Sie keine ParallelitätsprimitiveGo bietet benutzerfreundliche Parallelitätsprimitive, die aufgrund der gleichen Einfachheit manchmal auch zu einer Überbeanspruchung führen können. Zunächst sind wir besorgt über die Kanäle und das Synchronisierungspaket. Manchmal ist es verlockend, einen Kanal aus Ihrem Paket zu exportieren, damit andere ihn verwenden können. Außerdem besteht ein häufiger Fehler darin,
sync.Mutex
einzubetten, ohne es auf privat zu setzen. Dies ist wie üblich nicht immer schlecht, führt jedoch beim Testen Ihres Programms zu bestimmten Problemen.
Wenn Sie Kanäle exportieren, verkomplizieren Sie zusätzlich das Leben des Paketbenutzers, was sich nicht lohnt. Sobald der Kanal aus dem Paket exportiert wird, treten beim Testen desjenigen, der diesen Kanal verwendet, Schwierigkeiten auf. Für erfolgreiche Tests muss der Benutzer Folgendes wissen:
- Wenn Daten über den Kanal gesendet werden.
- Gab es Fehler beim Empfang von Daten?
- Wie spült ein Paket den Kanal nach Abschluss, wenn überhaupt?
- Wie verpacke ich eine Paket-API, damit Sie sie nicht direkt aufrufen?
Schauen Sie sich das Beispiel zum Lesen der Warteschlange an. Hier ist eine Beispielbibliothek, die aus der Warteschlange liest und dem Benutzer einen Feed zum Lesen bereitstellt.
type Reader struct {...} func (r *Reader) ReadChan() <-chan Msg {...}
Jetzt möchte Ihr Bibliotheksbenutzer einen Test für seinen Verbraucher implementieren:
func TestConsumer(t testing.T) { cons := &Consumer{ r: libqueue.NewReader(), } for msg := range cons.r.ReadChan() {
Der Benutzer kann dann entscheiden, dass die Abhängigkeitsinjektion eine gute Idee ist, und seine eigenen Nachrichten in den Kanal schreiben:
func TestConsumer(t testing.T, q queueIface) { cons := &Consumer{ r: q, } for msg := range cons.r.ReadChan() {
Warten Sie, was ist mit den Fehlern?
func TestConsumer(t testing.T, q queueIface) { cons := &Consumer{ r: q, } for { select { case msg := <-cons.r.ReadChan():
Jetzt müssen wir irgendwie Ereignisse generieren, um tatsächlich in diesen Stub zu schreiben, der das Verhalten der von uns verwendeten Bibliothek repliziert. Wenn die Bibliothek gerade die synchrone API geschrieben hat, können wir dem Client-Code die gesamte Parallelität hinzufügen, sodass das Testen einfacher wird.
func TestConsumer(t testing.T, q queueIface) { cons := &Consumer{ r: q, } msg, err := cons.r.ReadMsg()
Wenn Sie Zweifel haben, denken Sie daran, dass es immer einfach ist, dem Verbraucherpaket (Verbrauchspaket) Parallelität hinzuzufügen, und es nach dem Export aus der Bibliothek schwierig oder unmöglich ist, es zu entfernen. Und vor allem vergessen Sie nicht, in die Paketdokumentation zu schreiben, ob die Struktur / das Paket für den gleichzeitigen Zugriff auf mehrere Goroutinen sicher ist.
Manchmal ist es immer noch wünschenswert oder notwendig, den Kanal zu exportieren. Um einige der oben genannten Probleme zu mindern, können Sie Kanäle über Accessoren anstelle des direkten Zugriffs bereitstellen und diese nur zum Lesen (
←chan
) oder nur zum Schreiben (
chan←
) offen lassen, wenn Sie deklarieren.
Verwenden Sie net/http/httptest
Httptest
können
Httptest
http.Handler
Code ausführen, ohne einen Server zu starten oder an einen Port zu binden. Dies beschleunigt das Testen und ermöglicht es Ihnen, Tests zu geringeren Kosten parallel durchzuführen.
Hier ist ein Beispiel für denselben Test, der auf zwei Arten implementiert wurde. Hier gibt es nichts Großartiges, aber dieser Ansatz reduziert die Codemenge und spart Ressourcen.
func TestServe(t *testing.T) {
Das vielleicht wichtigste Merkmal ist, dass Sie mit
httptest
den Test nur in die Funktion
httptest
können, die Sie testen möchten. Keine Router, Middleware oder andere Nebenwirkungen, die beim Einrichten von Servern, Diensten, Prozessorfabriken, Prozessorfabriken oder anderen Dingen auftreten, die Sie für eine gute Idee halten.
Um dieses Prinzip in Aktion zu sehen, lesen Sie
den Artikel von
Marc Berger .
Verwenden Sie das separate Paket _test
Die meisten Tests im Ökosystem werden in den Dateien
pkg_test.go
, verbleiben jedoch im selben Paket:
package pkg
. Ein separates
foo_test.go
ist das Paket, das Sie in der neuen Datei
foo_test.go
im Verzeichnis des zu
foo_test.go
Moduls
foo/
mit dem Deklarationspaket
package foo_test
. Von hier aus können Sie
github.com/example/foo
und andere Abhängigkeiten importieren. Mit dieser Funktion können Sie viele Dinge tun. Dies ist die empfohlene Lösung für zyklische Abhängigkeiten in Tests. Sie verhindert das Auftreten von „spröden Tests“ und gibt dem Entwickler das Gefühl, wie es ist, ein eigenes Paket zu verwenden. Wenn Ihr Paket schwer zu verwenden ist, ist das Testen mit dieser Methode ebenfalls schwierig.
Diese Strategie verhindert fragile Tests, indem der Zugriff auf private Variablen eingeschränkt wird. Insbesondere wenn Ihre Tests unterbrochen werden und Sie separate Testpakete verwenden, ist fast garantiert, dass ein Client, der eine Funktion verwendet, die die Tests unterbricht, auch beim Aufruf unterbrochen wird.
Schließlich hilft es, Importzyklen in Tests zu vermeiden. Die meisten Pakete hängen eher von anderen Paketen ab, die Sie neben den Testpaketen geschrieben haben. Daher kommt es zu einer Situation, in der der Importzyklus auf natürliche Weise erfolgt. Ein externes Paket befindet sich über beiden Paketen in der Pakethierarchie. Nehmen Sie ein Beispiel aus der Programmiersprache Go (Kapitel 11, Abschnitt 2.4), in dem
net/url
einen URL-Parser implementiert, den
net/http
zur Verwendung importiert.
net / url
muss jedoch mit einem realen Anwendungsfall getestet werden, indem
net / http
importiert wird. Somit stellt sich
net/url_test
.
Wenn Sie jetzt ein separates Testpaket verwenden, benötigen Sie möglicherweise Zugriff auf nicht exportierte Entitäten in dem Paket, in dem sie zuvor verfügbar waren. Einige Entwickler sind zum ersten Mal damit konfrontiert, wenn sie etwas basierend auf der Zeit testen (z. B. time.Now wird mithilfe einer Funktion zu einem Stub). In diesem Fall können wir eine zusätzliche Datei verwenden, um Entitäten ausschließlich während des Testens bereitzustellen, da die
_test.go
Dateien von regulären Builds ausgeschlossen
_test.go
.
Woran müssen Sie sich erinnern?Es ist wichtig zu bedenken, dass keine der oben beschriebenen Methoden ein Allheilmittel ist. Der beste Ansatz in jedem Unternehmen besteht darin, die Situation kritisch zu analysieren und unabhängig die beste Lösung für das Problem auszuwählen.
Möchten Sie mehr über das Testen mit Go erfahren?
Lesen Sie diese Artikel:
Dave Cheneys schreibtischgesteuerte Tests in GoDas Kapitel Go Programming Language zum Testen.Oder schauen Sie sich diese Videos an:
Hashimotos Advanced Testing With Go-Vortrag von Gophercon 2017Andrew Gerrands Testtechniken sprechen von 2014Wir hoffen, diese Übersetzung hat Ihnen geholfen. Wir warten auf Kommentare und alle, die mehr über den Kurs erfahren möchten, laden Sie zum
Tag der
offenen Tür ein , der am 23. Mai stattfinden wird.