Gdzie idą testy jednostkowe Pythona?

490

Jeśli piszesz bibliotekę lub aplikację, dokąd idą pliki testów jednostkowych?

Miło jest oddzielić pliki testowe od głównego kodu aplikacji, ale niewygodne jest umieszczenie ich w podkatalogu „testy” w katalogu głównym aplikacji, ponieważ utrudnia importowanie modułów, które będziesz testować.

Czy jest tutaj najlepsza praktyka?

Tylko czytać
źródło
4
Istnieje wiele akceptowalnych odpowiedzi na to pytanie, co sprawia, że ​​nie jest to pytanie dotyczące strony z pytaniami. Ludzie muszą zrozumieć, że nie wszystkie świetne pomocne pytania można zadać na SO. Formatorzy wybierają właśnie to i musimy przestrzegać ich zasad.
Wouter J
121
Ludzie muszą również zrozumieć, że kiedy strony SO stają się hitem w wyszukiwarce Google, być może lepiej jest po prostu iść z prądem niż trzymać się oficjalnej doktryny SO.
Christopher Barber
6
@ChristopherBarber ludzie muszą również zrozumieć, że decyzje i agresywne zamykanie sprawiły, że ostatecznie zyskałem tak okropną reputację witryny, że wolałbym nie zadawać pytań, chyba że naprawdę, naprawdę nie mam lepszej opcji.
Stefano Borini,

Odpowiedzi:

200

W przypadku pliku module.pytest jednostkowy powinien być zwykle wywoływany test_module.pyzgodnie z konwencjami nazewnictwa w języku Python.

Istnieje kilka powszechnie akceptowanych miejsc do umieszczenia test_module.py:

  1. W tym samym katalogu co module.py.
  2. W ../tests/test_module.py(na tym samym poziomie co katalog kodu).
  3. W tests/test_module.py(jeden poziom w katalogu kodów).

Wolę numer 1 ze względu na prostotę znajdowania testów i ich importowania. Niezależnie od używanego systemu kompilacji można łatwo skonfigurować uruchamianie plików od test_. W rzeczywistości domyślnym unittestwzorcem używanym do wykrywania testów jesttest*.py .

użytkownik6868
źródło
12
Do protokołu load_tests wyszukuje pliki o nazwie test * .py domyślnie. Poza tym ten najlepszy wynik Google i ta najszczersza dokumentacja używają formatu test_module.py.
dfarrell07,
6
Korzystanie z foo_test.py pozwala zaoszczędzić naciśnięcie klawisza lub dwa podczas korzystania z uzupełniania tabulatorami, ponieważ nie masz wielu plików zaczynających się od „test_”.
jałowiec
11
@juniper, podążając za swoimi myślami moduł_test pojawiłby się podczas automatycznego uzupełniania podczas kodowania. To może być mylące lub denerwujące.
Medeiros,
11
Podczas wdrażania kodu nie chcemy wdrażać testów w naszych lokalizacjach produkcyjnych. Zatem posiadanie ich w jednym katalogu „./tests/test_blah.py” jest łatwe do zerwania, gdy wykonujemy wdrożenia. TAKŻE niektóre testy pobierają przykładowe pliki danych, a umieszczenie ich w katalogu testowym jest niezbędne, abyśmy nie wdrożyli danych testowych.
Kevin J. Rice
1
@ KevinJ.Rice Czy nie powinieneś testować działania kodu w lokalizacjach produkcyjnych?
endolith,
67

Tylko 1 plik testowy

Jeśli jest tylko 1 plik testowy, zaleca się umieszczenie go w katalogu najwyższego poziomu:

module/
    lib/
        __init__.py
        module.py
    test.py

Uruchom test w CLI

python test.py

Wiele plików testowych

Jeśli ma wiele plików testowych, umieść go w testsfolderze:

module/
    lib/
        __init__.py
        module.py
    tests/
        test_module.py
        test_module_function.py
# test_module.py

import unittest
from lib import module

class TestModule(unittest.TestCase):
    def test_module(self):
        pass

if __name__ == '__main__':
    unittest.main()

Uruchom test w CLI

# In top-level /module/ folder
python -m tests.test_module
python -m tests.test_module_function

Posługiwać się unittest discovery

unittest discovery znajdzie wszystkie testy w folderze pakietu.

Utwórz __init__.pyw tests/folderze

module/
    lib/
        __init__.py
        module.py
    tests/
        __init__.py
        test_module.py
        test_module_function.py

Uruchom test w CLI

# In top-level /module/ folder

# -s, --start-directory (default current directory)
# -p, --pattern (default test*.py)

python -m unittest discover

Odniesienie

Struktura testu jednostkowego

Steely Wing
źródło
51

Powszechną praktyką jest umieszczanie katalogu testów w tym samym katalogu nadrzędnym co moduł / pakiet. Więc jeśli moduł nazywał się foo.py, układ katalogu wyglądałby następująco:

parent_dir/
  foo.py
  tests/

Oczywiście nie ma na to jednego sposobu. Możesz także utworzyć podkatalog testów i zaimportować moduł za pomocą importu bezwzględnego .

Gdziekolwiek umieścisz swoje testy, polecam użyć nosa do ich przeprowadzenia. Nos przeszukuje twoje katalogi w poszukiwaniu testów. W ten sposób możesz przeprowadzać testy wszędzie tam, gdzie jest to najbardziej sensowne z organizacyjnego punktu widzenia.

Cristian
źródło
16
Chciałbym to zrobić, ale nie mogę tego zrobić. Aby uruchomić testy, jestem w katalogu parent_dir i wpisuję: „python test \ foo_test.py” oraz w foo_test.py: „z ..foo zaimportuj to, tamto” To nie powiedzie się z: „ValueError: Attempted Względny import w pakiecie innym niż Zarówno katalog nadrzędny, jak i testy mają w sobie plik init .py, więc nie jestem pewien, dlaczego nie są pakietami. Podejrzewam, że dzieje się tak, ponieważ skrypt najwyższego poziomu uruchamiany z wiersza poleceń nie może być uważany za część pakietu, nawet jeśli jest w katalogu z plikiem .py init . Jak więc uruchomić testy? Dzisiaj pracuję w systemie Windows, pobłogosław moje bawełniane skarpetki.
Jonathan Hartley,
4
Najlepszym sposobem - znalazłem - na przeprowadzenie testów jednostkowych jest zainstalowanie biblioteki / programu, a następnie uruchomienie testów jednostkowych za pomocą nosa. Poleciłbym virtualenv i virtualenvwrapper, aby było to o wiele łatwiejsze.
Cristian
@Tartley - potrzebujesz pliku .py init w katalogu „testy”, aby importowanie rozgrzeszenia działało. Mam tę metodę pracy z nosem, więc nie jestem pewien, dlaczego masz problemy.
cmcginty
4
Dzięki Casey - ale mam plik inicjujący .py we wszystkich odpowiednich katalogach. Nie wiem, co robię źle, ale mam ten problem we wszystkich moich projektach w języku Python i nie rozumiem, dlaczego nikt inny tego nie robi. Och, kochanie.
Jonathan Hartley
8
Jedno rozwiązanie dla mojego problemu, z Python2.6 lub nowszym, abyśmy uruchomili testy z katalogu głównego twojego projektu za pomocą: python -m project.package.tests.module_tests (zamiast python project / package / testy / module_tests.py) . To stawia katalog modułu testowego na ścieżce, dzięki czemu testy mogą następnie wykonać względny import do katalogu nadrzędnego, aby uzyskać testowany moduł.
Jonathan Hartley
32

Mieliśmy to samo pytanie, pisząc Pythoscope ( https://pypi.org/project/pythoscope/ ), który generuje testy jednostkowe programów w języku Python. Przed wybraniem katalogu zapytaliśmy ludzi na temat testowania na liście python, było wiele różnych opinii. Na koniec postanowiliśmy umieścić katalog „testy” w tym samym katalogu, co kod źródłowy. W tym katalogu generujemy plik testowy dla każdego modułu w katalogu nadrzędnym.

Paul Hildebrandt
źródło
2
Niesamowite! Właśnie uruchomiłem Pythoscope (świetne logo) na jakimś starszym kodzie i było niesamowicie. Natychmiastowe najlepsze praktyki i możesz zachęcić innych programistów do wypełnienia nieudanych testów. Teraz, aby podłączyć do bambusa? :)
Czy
27

Ja również zwykle umieszczam swoje testy jednostkowe w samym pliku, jak zauważa Jeremy Cantrell powyżej, chociaż zwykle nie umieszczam funkcji testowej w głównej części, ale raczej umieszczam wszystko w

if __name__ == '__main__':
   do tests...

blok. To kończy się dodaniem dokumentacji do pliku jako „przykładowego kodu” na temat używania testowanego pliku python.

Powinienem dodać, że mam tendencję do pisania bardzo ciasnych modułów / klas. Jeśli twoje moduły wymagają bardzo dużej liczby testów, możesz umieścić je w innym, ale nawet wtedy nadal dodam:

if __name__ == '__main__':
   import tests.thisModule
   tests.thisModule.runtests

Dzięki temu każdy, kto czyta kod źródłowy, wie, gdzie szukać kodu testowego.

Thomas Andrews
źródło
„jak Jeremy Cantrell powyżej notatek” gdzie?
endolith
Lubię ten sposób pracy. Najprostszy z edytorów można skonfigurować do uruchamiania pliku za pomocą klawisza skrótu, dzięki czemu podczas przeglądania kodu możesz natychmiast uruchomić testy. Niestety w językach innych niż Python może to wyglądać okropnie, więc jeśli jesteś w sklepie C ++ lub Java, twoi koledzy mogą się na to zgodzić. Nie działa również dobrze z narzędziami do pokrywania kodu.
Keeely
19

Co jakiś czas sprawdzam temat umieszczania testów i za każdym razem większość zaleca osobną strukturę folderów obok kodu biblioteki, ale stwierdzam, że za każdym razem argumenty są takie same i nie są tak przekonujące. Kładę moje moduły testowe gdzieś obok modułów podstawowych.

Głównym powodem tego jest: refaktoryzacja .

Kiedy się poruszam, chcę, aby moduły testowe poruszały się z kodem; łatwo jest przegrać testy, jeśli są w osobnym drzewie. Bądźmy szczerzy, prędzej czy później skończysz z zupełnie inną strukturą folderów, taką jak django , flask i wiele innych. Co jest w porządku, jeśli cię to nie obchodzi.

Główne pytanie, które powinieneś sobie zadać, brzmi:

Piszę:

  • a) biblioteka wielokrotnego użytku lub
  • b) budowanie projektu niż wiązanie razem częściowo rozdzielonych modułów?

Jeśli:

Osobny folder i dodatkowy wysiłek w celu utrzymania jego struktury mogą być bardziej odpowiednie. Nikt nie będzie narzekał na wdrożenie testów w produkcji .

Ale równie łatwo jest wykluczyć testy z dystrybucji, gdy są mieszane z folderami podstawowymi; umieść to w setup.py :

find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"]) 

Jeśli b:

Możesz życzyć - jak każdy z nas - że piszesz biblioteki wielokrotnego użytku, ale przez większość czasu ich życie jest związane z życiem projektu. Priorytetem powinna być możliwość łatwego utrzymania projektu.

Następnie, jeśli wykonałeś dobrą robotę, a twój moduł dobrze pasuje do innego projektu, prawdopodobnie zostanie skopiowany - nie rozwidlony lub utworzony w osobnej bibliotece - do tego nowego projektu i przeniesie testy, które leżą obok niego w tej samej strukturze folderów jest łatwe w porównaniu z testowaniem wyników w bałaganie, którym stał się osobny folder testowy. (Możesz argumentować, że nie powinien to być bałagan, ale bądźmy realistami).

Tak więc wybór należy do ciebie, ale twierdzę, że przy pomieszanych testach osiągasz te same rzeczy, co w osobnym folderze, ale przy mniejszym wysiłku w utrzymaniu porządku.

Janusz Skonieczny
źródło
1
na czym polega problem z wdrożeniem testów do produkcji? z mojego doświadczenia wynika, że ​​jest to bardzo przydatne: pozwala użytkownikom faktycznie uruchamiać testy w ich środowisku ... kiedy otrzymujesz raporty o błędach, możesz poprosić użytkownika o uruchomienie zestawu testów, aby upewnić się, że wszystko jest w porządku i wysłać gorące poprawki do testu apartament bezpośrednio ... Poza tym, to nie dlatego, umieścić swoje testy w module.tests że będzie kończyć się w produkcji, chyba że zrobił coś złego w pliku konfiguracyjnym ...
anarcat
2
Jak napisałem w odpowiedzi, zwykle nie rozdzielam testów. Ale. Umieszczenie testów w pakiecie dystrybucyjnym może prowadzić do wykonania rzeczy, których normalnie nie chciałbyś w środowisku produkcyjnym (np. Kodu na poziomie modułu). Zależy to oczywiście od tego, jak są napisane testy, ale aby zachować bezpieczeństwo, należy je pominąć, nikt przypadkowo nie uruchomi czegoś szkodliwego. Nie jestem przeciwny włączaniu testów do dystrybucji, ale rozumiem, że z zasady jest to bezpieczniejsze. A umieszczenie ich w osobnym folderze sprawia, że ​​bardzo łatwo jest nie dołączać ich do dist.
Janusz Skonieczny
15

Korzystam z tests/katalogu, a następnie importuję główne moduły aplikacji za pomocą importu względnego. Tak więc w MyApp / testy / foo.py mogą być:

from .. import foo

aby zaimportować MyApp.foomoduł.

John Millikin
źródło
5
„ValueError: Próba względnego importu w pakiecie innym”
około
12

Nie wierzę, że istnieje „najlepsza praktyka”.

Umieszczam swoje testy w innym katalogu poza kodem aplikacji. Następnie dodaję główny katalog aplikacji do sys.path (umożliwiając importowanie modułów z dowolnego miejsca) w moim skrypcie testera (który robi również inne rzeczy) przed uruchomieniem wszystkich testów. W ten sposób nigdy nie muszę usuwać katalogu testów z głównego kodu, gdy go wypuszczam, co oszczędza mi czasu i wysiłku, nawet jeśli jest to bardzo mała ilość.

dwestbrook
źródło
3
Następnie dodaję główny katalog aplikacji do sys.path Problem polega na tym, że twoje testy zawierają moduły pomocnicze (takie jak testowy serwer WWW) i musisz zaimportować takie moduły pomocnicze z odpowiednich testów.
Piotr Dobrogost
Tak to wygląda dla mnie:os.sys.path.append(os.dirname('..'))
Matt M.
11

Z mojego doświadczenia w tworzeniu frameworków testujących w Pythonie sugerowałbym umieszczenie testów jednostkowych Pythona w osobnym katalogu. Zachowaj symetryczną strukturę katalogów. Byłoby to pomocne w pakowaniu tylko podstawowych bibliotek, a nie w pakowanie testów jednostkowych. Poniżej zaimplementowano schemat.

                              <Main Package>
                               /          \
                              /            \
                            lib           tests
                            /                \
             [module1.py, module2.py,  [ut_module1.py, ut_module2.py,
              module3.py  module4.py,   ut_module3.py, ut_module.py]
              __init__.py]

W ten sposób, gdy pakujesz te biblioteki za pomocą rpm, możesz po prostu spakować główne moduły biblioteczne (tylko). Pomaga to w utrzymaniu, szczególnie w zwinnym środowisku.

Rahul Biswas
źródło
1
Zdałem sobie sprawę, że jedną z potencjalnych zalet tego podejścia jest to, że testy można opracować (a może nawet kontrolować wersję) jako samodzielną aplikację. Oczywiście, z każdą zaletą wiąże się wada - utrzymanie symetrii polega zasadniczo na „księgowaniu podwójnego zapisu” i sprawia, że ​​refaktoryzacja jest bardziej uciążliwa.
Jasha
Miękkie pytanie uzupełniające: dlaczego zwinne środowisko jest szczególnie odpowiednie do tego podejścia? (tj. co ze sprawnym przepływem pracy sprawia, że ​​symetryczna struktura katalogów jest tak korzystna?)
Jasha 1'18
11

Polecam sprawdzenie niektórych głównych projektów Pythona na GitHub i uzyskanie pomysłów.

Kiedy twój kod się powiększy i dodasz więcej bibliotek, lepiej jest utworzyć folder testowy w tym samym katalogu, w którym znajduje się setup.py i utworzyć kopię lustrzaną struktury katalogu projektu dla każdego typu testu (unittest, integracja, ...)

Na przykład, jeśli masz strukturę katalogów, taką jak:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
setup.py

Po dodaniu folderu testowego będziesz miał strukturę katalogów, takich jak:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
test/
   unit/
      myapp/
         moduleA/
            module_A_test.py
         moduleB/
            module_B_test.py
   integration/
          myapp/
             moduleA/
                module_A_test.py
             moduleB/
                module_B_test.py
setup.py

Wiele poprawnie napisanych pakietów Pythona używa tej samej struktury. Bardzo dobrym przykładem jest pakiet Boto. Sprawdź https://github.com/boto/boto

Wysypka
źródło
1
Zaleca się stosowanie „testów” zamiast „test”, ponieważ „test” jest wbudowanym modułem Pythona. docs.python.org/2/library/test.html
brodul
Nie zawsze .. na przykład matplotlibma to pod matplotlib/lib/matplotlib/tests( github.com/matplotlib/matplotlib/tree/… ), sklearnma to pod scikitelearn/sklearn/tests( github.com/scikit-learn/scikit-learn/tree/master/sklearn/tests )
alpha_989
7

Jak to robię...

Struktura folderów:

project/
    src/
        code.py
    tests/
    setup.py

Setup.py wskazuje src / jako lokalizację zawierającą moduły moich projektów, a następnie uruchamiam:

setup.py develop

Co dodaje mój projekt do pakietów stron, wskazując na moją kopię roboczą. Do uruchomienia moich testów używam:

setup.py tests

Za pomocą dowolnego testera, który skonfigurowałem.

Dale Reidy
źródło
Wygląda na to, że przesłoniłeś termin „moduł”. Większość programistów Pythona prawdopodobnie pomyślałaby, że moduł jest plikiem, który wywołałeś code.py. Bardziej sensowne byłoby nazywanie katalogu najwyższego poziomu „projektem”.
blokeley,
4

Wolę katalog testów najwyższego poziomu. Oznacza to, że import staje się nieco trudniejszy. Do tego mam dwa rozwiązania:

  1. Użyj setuptools. Następnie można przejść test_suite='tests.runalltests.suite'do setup(), a może po prostu uruchomić testy:python setup.py test
  2. Ustaw PYTHONPATH podczas uruchamiania testów: PYTHONPATH=. python tests/runalltests.py

Oto jak te rzeczy są obsługiwane przez kod w M2Crypto:

Jeśli wolisz przeprowadzać testy z testami nosa, może być konieczne wykonanie czegoś nieco innego.

bstpierre
źródło
3

Używamy

app/src/code.py
app/testing/code_test.py 
app/docs/..

W każdym pliku testowego możemy wstawić ../src/w sys.path. To nie jest najlepsze rozwiązanie, ale działa. Myślę, że byłoby wspaniale, gdyby ktoś pojawił się w Javie z czymś takim jak maven, co daje standardowe konwencje, które działają, bez względu na projekt nad którym pracujesz.

André
źródło
1

Jeśli testy są proste, po prostu umieść je w dokumentacji - większość frameworków testowych dla Pythona będzie w stanie tego użyć:

>>> import module
>>> module.method('test')
'testresult'

W przypadku innych bardziej zaangażowanych testów umieściłem je w ../tests/test_module.pylub w tests/test_module.py.


źródło
1

W języku C # ogólnie dzieliłem testy na osobny zestaw.

W Pythonie - do tej pory - miałem tendencję do pisania doctestów, gdzie test znajduje się w dokumentacji funkcji, lub umieszczania ich w if __name__ == "__main__"bloku na dole modułu.

George V. Reilly
źródło
0

Pisząc pakiet o nazwie „foo”, umieszczę testy jednostkowe w osobnym pakiecie „foo_test”. Moduły i podpakiety będą wtedy miały taką samą nazwę jak moduł pakietu SUT. Np. Testy dla modułu foo.xy znajdują się w foo_test.xy Pliki __init__.py każdego pakietu testowego zawierają następnie pakiet AllTests, który zawiera wszystkie pakiety testowe pakietu. setuptools zapewnia wygodny sposób na określenie głównego pakietu testowego, dzięki czemu po „python setup.py develop” możesz po prostu użyć „python setup.py test” lub „python setup.py test -s foo_test.x.SomeTestSuite” do tylko określony pakiet.

Sebastian Rittau
źródło
0

Testy umieszczam w tym samym katalogu, co testowany kod (CUT); na foo.pytesty będąfoo_ut.py lub podobne. (Poprawiam proces wykrywania testów, aby je znaleźć.)

To umieszcza testy obok kodu na liście katalogów, co sprawia, że ​​testy są dostępne i sprawia, że ​​otwieranie testów jest tak proste, jak to możliwe, gdy są one w osobnym pliku. (W przypadku edytorów wiersza poleceńvim foo* i podczas korzystania z graficznej przeglądarki systemu plików wystarczy kliknąć plik CUT, a następnie bezpośrednio sąsiadujący plik testowy.)

Jak zauważyli inni , ułatwia to również refaktoryzację i wyodrębnienie kodu do użycia w innym miejscu, jeśli zajdzie taka potrzeba.

Naprawdę nie podoba mi się pomysł umieszczania testów w zupełnie innym drzewie katalogów; dlaczego utrudniać programistom otwieranie testów podczas otwierania pliku za pomocą CUT? To nie jest tak, że zdecydowana większość programistów tak chętnie pisze lub poprawia testy, że zignorują wszelkie bariery, zamiast je usprawiedliwiać. (Wręcz przeciwnie, z mojego doświadczenia; nawet jeśli uczynisz to tak łatwym, jak to możliwe, znam wielu programistów, którym nie przeszkadza pisanie testów.)

cjs
źródło
-2

Niedawno zacząłem programować w Pythonie, więc tak naprawdę nie miałem okazji znaleźć najlepszych praktyk. Ale napisałem moduł, który wyszukuje i uruchamia wszystkie testy.

Więc mam:

aplikacja /
 appfile.py
test/
 appfileTest.py

Będę musiał zobaczyć, jak to pójdzie, gdy przejdę do większych projektów.

quamrana
źródło
4
Cześć, camelCase jest trochę dziwny w świecie python.
Stuart Axon