Richtige Paketbenennung zum Testen mit der Go-Sprache

102

Ich habe in Go verschiedene Benennungsstrategien für Testpakete gesehen und wollte wissen, welche Vor- und Nachteile die einzelnen sind und welche ich verwenden sollte.

Strategie 1:

Dateiname: github.com/user/myfunc.go

package myfunc

Name der Testdatei: github.com/user/myfunc_test.go

package myfunc

Ein Beispiel finden Sie in bzip2 .

Strategie 2:

Dateiname: github.com/user/myfunc.go

package myfunc

Name der Testdatei: github.com/user/myfunc_test.go

package myfunc_test

import (
    "github.com/user/myfunc"
)

Ein Beispiel finden Sie unter Draht .

Strategie 3:

Dateiname: github.com/user/myfunc.go

package myfunc

Name der Testdatei: github.com/user/myfunc_test.go

package myfunc_test

import (
    . "myfunc"
)

Ein Beispiel finden Sie unter Zeichenfolgen .

Die Go-Standardbibliothek scheint eine Mischung aus Strategie 1 und 2 zu verwenden. Welche der drei sollte ich verwenden? Es ist ein Schmerz, der package *_testan meine Testpakete angehängt wird, da dies bedeutet, dass ich meine privaten Paketmethoden nicht testen kann, aber vielleicht gibt es einen versteckten Vorteil, den ich nicht kenne?

Dan
quelle
9
Diese Frage wird nur zu unterschiedlichen Meinungen führen, aber ich werde meine einwerfen. Sie sollten Ihre privaten Methoden nicht testen müssen. Sie möchten die Schnittstelle Ihres Pakets testen, die andere Entwickler verwenden. Wenn die Tests fehlschlagen, wissen Sie, dass Ihre privaten Methoden überprüft werden müssen.
Brenden
2
Das Beispiel [wire] ( github.com/btcsuite/btcd/blob/master/wire/msgtx_test.go ) für Strategie 2 ist jetzt auch ein Beispiel für Strategie 1 ...
durp

Antworten:

130

Der grundlegende Unterschied zwischen den drei von Ihnen aufgelisteten Strategien besteht darin, ob sich der Testcode im selben Paket befindet wie der zu testende Code. Die Entscheidung für die Verwendung package myfuncoder package myfunc_testin der Testdatei hängt davon ab, ob Sie eine White-Box oder eine Black-Box ausführen möchten Tests .

Es ist nichts Falsches daran, beide Methoden in einem Projekt zu verwenden. Zum Beispiel könnten Sie myfunc_whitebox_test.gound habenmyfunx_blackbox_test.go .

Testcode-Paketvergleich

  • Black-Box-Test: Verwenden Sie package myfunc_testdiese Option, um sicherzustellen, dass Sie nur die exportierten Bezeichner verwenden .
  • White-Box-Test: Verwenden package myfuncSie diese Option, damit Sie Zugriff auf die nicht exportierten Bezeichner haben. Geeignet für Unit-Tests, die Zugriff auf nicht exportierte Variablen, Funktionen und Methoden erfordern.

Vergleich der in Frage gestellten Strategien

  • Strategie 1: Die Datei myfunc_test.goverwendet package myfunc- In diesem Fall befindet sich der Testcode in myfunc_test.godemselben Paket wie der Code, in dem getestet wirdmyfunc.go wird ( myfuncin diesem Beispiel).
  • Strategie 2: Die Datei myfunc_test.goverwendet package myfunc_test- In diesem Fall wird der Testcode in myfunc_test.go"als separates Paket kompiliert und dann mit der Haupttest-Binärdatei verknüpft und ausgeführt". [Quelle: Zeilen 58–59 in der Quellcode test.go ]
  • Strategie 3: Die Datei myfunc_test.goverwendet package myfunc_testaber importiert myfuncmit der Punktnotation - Dies ist eine Variante von Strategie 2, verwendet jedoch die Punktnotation zum Importieren myfunc.
Matthew Rankin
quelle
1
Es ist zu beachten, dass bei Verwendung von Strategie 1 auch Dateien _test.gogetrennt von dem zu testenden Paket aufbewahrt werden (dasselbe Verhalten wie bei Strategie 2). Dies scheint nicht per github.com/golang/go/issues/15315
Kevin Deenanauth
Ich habe gesehen, dass ein starkes Paket Strategie 3 verwendet, aber ich kann nicht verstehen, worum es geht.
PickBoy
1
Ich habe ein Paket gegabelt und Änderungen vorgenommen, und jetzt versuchen alle meine Tests, das ursprüngliche Repo anstelle meines gegabelten Pakets zu importieren. Mit Strategie 3 muss ich "github.com/original/link" nicht in "github.com/my/fork" ändern, da es nur auf "verweist". stattdessen.
Nmarley
1
@ KevinDeenanauth Das hat mich nur überrascht. Ich dachte, ich hätte eine Falle gefunden, als ich gerade eine _test.gomit einem Nicht- _testPaketnamen gefunden habe, der eine enthält, die eine func init()globale Paketvariable zum Testen ändert. Ich lag falsch.
Zyl
1
@nmarley das .löst dein Gabelproblem nicht. Es ist kein relativer Import. Es importiert nur die Bezeichner "in das aktuelle Paket".
Qaisjp
19

Dies hängt vom Umfang Ihrer Tests ab. Tests auf hoher Ebene (Integration, Akzeptanz usw.) sollten wahrscheinlich in einem separaten Paket abgelegt werden, um sicherzustellen, dass Sie das Paket über die exportierte API verwenden.

Wenn Sie ein großes Paket mit vielen Interna haben, die getestet werden müssen, verwenden Sie dasselbe Paket für Ihre Tests. Dies ist jedoch keine Einladung für Ihre Tests, auf einen privaten Status zuzugreifen. Das würde das Refactoring zu einem Albtraum machen. Wenn ich Strukturen in go schreibe, implementiere ich oft Schnittstellen. Es sind diese Schnittstellenmethoden, die ich aus meinen Tests aufrufe, nicht alle Hilfsmethoden / -funktionen einzeln.

mdwhatcott
quelle
13

Sie sollten nach Möglichkeit Strategie 1 verwenden. Sie können den speziellen foo_testPaketnamen verwenden, um Importzyklen zu vermeiden. Dies ist jedoch meistens der Fall, damit die Standardbibliothek mit demselben Mechanismus getestet werden kann. Zum Beispiel stringskann nicht mit Strategie 1 getestet werden , da das testingPaket hängt davon ab strings. Wie Sie sagten, haben Sie mit Strategie 2 oder 3 keinen Zugriff auf die privaten Kennungen des Pakets. Daher ist es normalerweise besser, es nicht zu verwenden, es sei denn, Sie müssen.

guelfey
quelle
10
Wie ist es keine Tugend, in Tests keinen Zugriff auf private Kennungen zu haben?
Jub0bs
3
Gemäß bewährten Testpraktiken testen Sie keine internen Implementierungsdetails für ein Code-Artefakt. Sohn zu tun, ist ein Code-Geruch
Gerardo Lima
0

Ein wichtiger Hinweis, den ich gerne import .von Golang CodeReviewComments hinzufügen möchte :

Das import .Formular kann bei Tests hilfreich sein, die aufgrund zirkulärer Abhängigkeiten nicht Teil des zu testenden Pakets sein können:

package foo_test

import (
    "bar/testutil" // also imports "foo"
    . "foo"
)

In diesem Fall kann sich die Testdatei nicht im Paket foo befinden, da sie verwendet wird bar/testutil , wodurch foo importiert wird. Also verwenden wir den 'Import'. Formular, damit die Datei vorgibt, Teil des Pakets foo zu sein, obwohl dies nicht der Fall ist.

Mit Ausnahme dieses einen Falles nichtimport . in Ihren Programmen verwenden. Dies erschwert das Lesen der Programme erheblich, da unklar ist, ob ein Name wie Quux im aktuellen Paket oder in einem importierten Paket eine Kennung der obersten Ebene darstellt.

Eric
quelle