Właściwe nazewnictwo pakietów do testowania w języku Go

103

Widziałem kilka różnych strategii nazewnictwa pakietów testowych w Go i chciałem wiedzieć, jakie są zalety i wady każdego z nich i którego powinienem użyć.

Strategia 1:

Nazwa pliku: github.com/user/myfunc.go

package myfunc

Nazwa pliku testowego: github.com/user/myfunc_test.go

package myfunc

Zobacz przykład bzip2 .

Strategia 2:

Nazwa pliku: github.com/user/myfunc.go

package myfunc

Nazwa pliku testowego: github.com/user/myfunc_test.go

package myfunc_test

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

Zobacz przykład drutu .

Strategia 3:

Nazwa pliku: github.com/user/myfunc.go

package myfunc

Nazwa pliku testowego: github.com/user/myfunc_test.go

package myfunc_test

import (
    . "myfunc"
)

Zobacz ciągi dla przykładu.

Wydaje się, że biblioteka standardowa Go używa kombinacji strategii 1 i 2. Której z trzech opcji powinienem użyć? Jest to uciążliwe dołączanie package *_testdo moich pakietów testowych, ponieważ oznacza to, że nie mogę przetestować prywatnych metod mojego pakietu, ale może istnieje ukryta zaleta, której nie jestem świadomy?

Dan
źródło
9
To pytanie będzie skutkowało tylko różnymi opiniami, ale dorzucę moją. Nie powinieneś testować swoich prywatnych metod. Chcesz przetestować interfejs swojego pakietu, z którego będą korzystać inni programiści. Jeśli testy nie powiodą się, wiesz, że Twoje prywatne metody wymagają spojrzenia.
Brenden
2
Przykład [wire] ( github.com/btcsuite/btcd/blob/master/wire/msgtx_test.go ) dla Strategii 2 jest teraz również przykładem Strategii 1 ...
durp

Odpowiedzi:

133

Podstawowa różnica między trzema wymienionymi strategiami polega na tym, czy kod testowy znajduje się w tym samym pakiecie co testowany kod. Decyzję o użyciu package myfunclub package myfunc_testw pliku testowego zależy od tego, czy chcesz wykonać białoskrzynkowe lub czarnej skrzynki testowania.

Nie ma nic złego w używaniu obu metod w projekcie. Na przykład możesz mieć myfunc_whitebox_test.goi myfunx_blackbox_test.go.

Porównanie pakietów kodów testowych

  • Testowanie czarnoskrzynkowe: Użyj package myfunc_test, które zapewni, że używasz tylko wyeksportowanych identyfikatorów .
  • Testowanie białoskrzynkowe: Użyj package myfunc, aby mieć dostęp do niewyeksportowanych identyfikatorów. Dobre dla testów jednostkowych, które wymagają dostępu do nieeksportowanych zmiennych, funkcji i metod.

Porównanie strategii wymienionych w pytaniu

  • Strategia 1: plik myfunc_test.goużywa package myfunc- w tym przypadku kod testowy myfunc_test.gobędzie w tym samym pakiecie co testowany kod myfunc.go, który jest myfuncw tym przykładzie.
  • Strategia 2: Plik myfunc_test.goużywa package myfunc_test- w tym przypadku kod testowy w myfunc_test.go„zostanie skompilowany jako oddzielny pakiet, a następnie połączony i uruchomiony z głównym testowym plikiem binarnym”. [Źródło: wiersze 58–59 w kodzie źródłowym test.go ]
  • Strategia 3: Plik myfunc_test.goużywa, package myfunc_testale importuje myfuncprzy użyciu notacji z kropkami - jest to wariant strategii 2, ale używa notacji kropkowej do importu myfunc.
Matthew Rankin
źródło
1
Należy zauważyć, że użycie Strategii 1 zachowa również pliki z _test.gooddzielnym pakietem niż testowany pakiet (takie samo zachowanie jak w Strategii 2). Wydaje się, że nie jest to udokumentowane na github.com/golang/go/issues/15315
Kevin Deenanauth
Widziałem, że pakiet używa strategii 3, ale nie rozumiem, o co chodzi?
PickBoy
1
Rozwidliłem pakiet i wprowadziłem zmiany, a teraz wszystkie moje testy próbują zaimportować oryginalne repozytorium zamiast mojego rozwidlonego pakietu. Dzięki Strategii 3 nie muszę zmieniać adresu „github.com/original/link” na „github.com/my/fork”, ponieważ odnosi się tylko do „.” zamiast.
nmarley
1
@KevinDeenanauth To mnie właśnie zaskoczyło. Wydawało mi się, że znalazłem pułapkę, kiedy właśnie znalazłem plik _test.goo _testnazwie niebędącej pakietem zawierającej a, func init()który zmienia jakąś globalną zmienną pakietu do testowania. Myliłem się.
Zyl
1
@nmarley .nie rozwiązuje problemu z widelcem. To nie jest względny import. Po prostu importuje identyfikatory „do bieżącego pakietu”.
qaisjp
19

To zależy od zakresu twoich testów. Testy wysokiego poziomu (integracja, akceptacja itp.) Powinny być prawdopodobnie umieszczone w osobnym pakiecie, aby upewnić się, że używasz pakietu za pośrednictwem wyeksportowanego API.

Jeśli masz duży pakiet z wieloma elementami wewnętrznymi, które muszą zostać przetestowane, użyj tego samego pakietu do testów. Ale to nie jest zaproszenie do twoich testów, aby uzyskać dostęp do jakiegokolwiek stanu prywatnego. To uczyniłoby refaktoryzację koszmarem. Kiedy piszę struktury w go , często implementuję interfejsy. To te metody interfejsu wywołuję z moich testów, a nie wszystkie metody / funkcje pomocnika z osobna.

mdwhatcott
źródło
13

Powinieneś używać strategii 1, kiedy tylko jest to możliwe. Możesz użyć specjalnej foo_testnazwy pakietu, aby uniknąć cykli importu, ale to głównie tam, więc standardowa biblioteka może być testowana za pomocą tego samego mechanizmu. Na przykład stringsnie można przetestować strategii 1, ponieważ testingpakiet zależy od strings. Jak powiedziałeś, ze strategią 2 lub 3 nie masz dostępu do prywatnych identyfikatorów pakietu, więc zwykle lepiej go nie używać, chyba że musisz.

guelfey
źródło
10
Dlaczego brak dostępu do prywatnych identyfikatorów w testach nie jest zaletą?
jub0bs
3
zgodnie z dobrymi praktykami testowymi nie testujesz wewnętrznych szczegółów implementacji pod kątem artefaktu kodu; robi syn, to zapach kodu
Gerardo Lima
0

Jedna ważna uwaga, o której chciałbym dodać import .z Golang CodeReviewComments :

import .Forma może być użyteczne w badaniach, że ze względu na koliste zależności, nie mogą być wykonane w skład pakietu testowany:

package foo_test

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

W tym przypadku plik testowy nie może znajdować się w pakiecie foo, ponieważ używa bar/testutil, który importuje foo. Dlatego używamy terminu „import”. form, aby plik udawał, że jest częścią pakietu foo, nawet jeśli tak nie jest.

Z wyjątkiem tego jednego przypadku, nie używajimport . w swoich programach. To sprawia, że ​​programy są znacznie trudniejsze do odczytania, ponieważ nie jest jasne, czy nazwa taka jak Quux jest identyfikatorem najwyższego poziomu w bieżącym pakiecie, czy w pakiecie importowanym.

Eric
źródło