Szukaj…


Konfigurowanie testu py

py.test jest jedną z kilku bibliotek testujących stron trzecich, które są dostępne dla Pythona. Można go zainstalować za pomocą pip z

pip install pytest

Kod do przetestowania

Powiedzmy, że testujemy funkcję dodawania w projectroot/module/code.py :

# projectroot/module/code.py
def add(a, b):
    return a + b

Kod testowy

Plik testowy projectroot/tests/test_code.py w projectroot/tests/test_code.py . Plik musi zaczynać się od test_ aby został rozpoznany jako plik testowy.

# projectroot/tests/test_code.py
from module import code


def test_add():
    assert code.add(1, 2) == 3

Uruchamianie testu

Z projectroot po prostu uruchamiamy py.test :

# ensure we have the modules
$ touch tests/__init__.py
$ touch module/__init__.py
$ py.test
================================================== test session starts ===================================================
platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /projectroot, inifile:
collected 1 items

tests/test_code.py .

================================================ 1 passed in 0.01 seconds ================================================

Nieudane testy

Niepowodzenie testu zapewni przydatne informacje o tym, co poszło źle:

# projectroot/tests/test_code.py
from module import code


def test_add__failing():
    assert code.add(10, 11) == 33

Wyniki:

$ py.test
================================================== test session starts ===================================================
platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /projectroot, inifile:
collected 1 items

tests/test_code.py F

======================================================== FAILURES ========================================================
___________________________________________________ test_add__failing ____________________________________________________

    def test_add__failing():
>       assert code.add(10, 11) == 33
E       assert 21 == 33
E        +  where 21 = <function add at 0x105d4d6e0>(10, 11)
E        +    where <function add at 0x105d4d6e0> = code.add

tests/test_code.py:5: AssertionError
================================================ 1 failed in 0.01 seconds ================================================

Wprowadzenie do testowania urządzeń

Bardziej skomplikowane testy czasami wymagają skonfigurowania rzeczy przed uruchomieniem kodu, który chcesz przetestować. Można to zrobić w samej funkcji testowej, ale w końcu duże funkcje testowe robią tyle, że trudno jest powiedzieć, gdzie kończy się konfiguracja i rozpoczyna się test. Możesz także uzyskać wiele zduplikowanych kodów konfiguracyjnych między różnymi funkcjami testowymi.

Nasz plik kodu:

# projectroot/module/stuff.py
class Stuff(object):
    def prep(self):
        self.foo = 1
        self.bar = 2

Nasz plik testowy:

# projectroot/tests/test_stuff.py
import pytest
from module import stuff


def test_foo_updates():
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    assert 1 == my_stuff.foo
    my_stuff.foo = 30000
    assert my_stuff.foo == 30000


def test_bar_updates():
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    assert 2 == my_stuff.bar
    my_stuff.bar = 42
    assert 42 == my_stuff.bar

Są to dość proste przykłady, ale gdyby nasz obiekt Stuff wymagał znacznie większej konfiguracji, stałoby się niewygodne. Widzimy, że między naszymi przypadkami testowymi znajduje się zduplikowany kod, więc najpierw przefiltrujmy to w osobną funkcję.

# projectroot/tests/test_stuff.py
import pytest
from module import stuff


def get_prepped_stuff():
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    return my_stuff


def test_foo_updates():
    my_stuff = get_prepped_stuff()
    assert 1 == my_stuff.foo
    my_stuff.foo = 30000
    assert my_stuff.foo == 30000


def test_bar_updates():
    my_stuff = get_prepped_stuff()
    assert 2 == my_stuff.bar
    my_stuff.bar = 42
    assert 42 == my_stuff.bar

Wygląda to lepiej, ale nadal mamy my_stuff = get_prepped_stuff() zaśmiecające nasze funkcje testowe.

py.test osprzęt na ratunek!

Urządzenia są znacznie bardziej wydajnymi i elastycznymi wersjami funkcji konfiguracji testowej. Mogą zrobić znacznie więcej niż wykorzystujemy tutaj, ale zrobimy to krok po kroku.

Najpierw zmieniamy get_prepped_stuff na urządzenie o nazwie prepped_stuff . Chcesz nazwać swoje urządzenia rzeczownikami, a nie czasownikami, ponieważ urządzenia te później zostaną użyte w samych funkcjach testowych. @pytest.fixture wskazuje, że ta konkretna funkcja powinna być obsługiwana raczej jako urządzenie niż zwykła funkcja.

@pytest.fixture
def prepped_stuff():
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    return my_stuff

Teraz powinniśmy zaktualizować funkcje testowe, aby korzystały z urządzenia. Odbywa się to poprzez dodanie parametru do definicji, który dokładnie odpowiada nazwie urządzenia. Po uruchomieniu py.test uruchomi urządzenie przed uruchomieniem testu, a następnie przekaże wartość zwracaną urządzenia do funkcji testowej przez ten parametr. (Zauważ, że urządzenia nie muszą zwracać wartości; zamiast tego mogą wykonywać inne czynności związane z konfiguracją, takie jak wywoływanie zewnętrznego zasobu, porządkowanie w systemie plików, wprowadzanie wartości do bazy danych, niezależnie od testów potrzebnych do instalacji)

def test_foo_updates(prepped_stuff):
    my_stuff = prepped_stuff
    assert 1 == my_stuff.foo
    my_stuff.foo = 30000
    assert my_stuff.foo == 30000


def test_bar_updates(prepped_stuff):
    my_stuff = prepped_stuff
    assert 2 == my_stuff.bar
    my_stuff.bar = 42
    assert 42 == my_stuff.bar

Teraz możesz zobaczyć, dlaczego nazwaliśmy go rzeczownikiem. ale my_stuff = prepped_stuff jest prawie bezużyteczna, więc zamiast tego prepped_stuff bezpośrednio prepped_stuff .

def test_foo_updates(prepped_stuff):
    assert 1 == prepped_stuff.foo
    prepped_stuff.foo = 30000
    assert prepped_stuff.foo == 30000


def test_bar_updates(prepped_stuff):
    assert 2 == prepped_stuff.bar
    prepped_stuff.bar = 42
    assert 42 == prepped_stuff.bar

Teraz używamy urządzeń! Możemy pójść dalej, zmieniając zakres urządzenia (aby działał tylko raz na moduł testowy lub sesję wykonywania zestawu testów zamiast raz na funkcję testową), budując urządzenia, które używają innych urządzeń, parametryzując urządzenie (tak, aby urządzenie i wszystkie testy przy użyciu tego urządzenia są uruchamiane wiele razy, raz dla każdego parametru przekazanego urządzeniu), urządzenia, które odczytują wartości z modułu, który je wywołuje ... jak wspomniano wcześniej, urządzenia mają znacznie większą moc i elastyczność niż normalna funkcja konfiguracji.

Czyszczenie po zakończeniu testów.

Powiedzmy, że nasz kod urósł, a nasz obiekt Stuff wymaga teraz specjalnego czyszczenia.

# projectroot/module/stuff.py
class Stuff(object):
def prep(self):
    self.foo = 1
    self.bar = 2

def finish(self):
    self.foo = 0
    self.bar = 0

Możemy dodać trochę kodu, aby wywołać czyszczenie na dole każdej funkcji testowej, ale urządzenia zapewniają lepszy sposób na zrobienie tego. Jeśli dodasz funkcję do urządzenia i zarejestrujesz ją jako finalizator , kod w funkcji finalizatora zostanie wywołany po zakończeniu testu przy użyciu urządzenia. Jeśli zakres urządzenia jest większy niż pojedyncza funkcja (jak moduł lub sesja), finalizator zostanie wykonany po zakończeniu wszystkich testów w zakresie, a więc po zakończeniu modułu lub po zakończeniu całej sesji testowej .

@pytest.fixture
def prepped_stuff(request):  # we need to pass in the request to use finalizers
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    def fin():  # finalizer function
        # do all the cleanup here
        my_stuff.finish()
    request.addfinalizer(fin)  # register fin() as a finalizer
    # you can do more setup here if you really want to
    return my_stuff

Korzystanie z funkcji finalizatora wewnątrz funkcji może być nieco trudne do zrozumienia na pierwszy rzut oka, szczególnie w przypadku bardziej skomplikowanych urządzeń. Zamiast tego możesz użyć urządzenia wydajności, aby zrobić to samo z przepływem wykonywania bardziej czytelnym dla człowieka. Jedyną prawdziwą różnicą jest to, że zamiast używania return używamy yield w części urządzenia, w której odbywa się konfiguracja, a kontrola powinna przejść do funkcji testowej, a następnie dodać cały kod czyszczenia po yield . yield_fixture go również jako yield_fixture , aby py.test wiedział, jak sobie z tym poradzić.

@pytest.yield_fixture
def prepped_stuff():  # it doesn't need request now!
    # do setup
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    # setup is done, pass control to the test functions
    yield my_stuff
    # do cleanup 
    my_stuff.finish()

I na tym kończy się wprowadzenie do testowania urządzeń!

Aby uzyskać więcej informacji, zobacz oficjalną dokumentację urządzeń py.test i oficjalną dokumentację urządzeń wydajności



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow