pyTest #1: stwórz własny fixture

Jeżeli pytest będzie wykorzystywanym przez was frameworkiem, fixtury będą wam towarzyszyć na każdym kroku. Fixture od strony pythona jest po prostu dekoratorem. Możemy nim 'udekorować’ nasze testy. Ale może bardziej poważnie… Używając fixtury możemy wykonać fragment kodu nie tylko po i przed testami, ale nawet przed i po pojedynczym teście (niezależnie od wyniku) lub przekazać obiekt, który będzie można wykorzystywany później w testach.

pyTest

pyTest: stwórz własny fixture #1


Jeżeli pytest będzie wykorzystywanym przez was frameworkiem, fixtury będą wam towarzyszyć na każdym kroku. Fixture od strony pythona jest po prostu dekoratorem. Możemy nim ‚udekorować’ nasze testy. Ale może bardziej poważnie… Używając fixtury możemy wykonać fragment kodu nie tylko po i przed testami, ale nawet przed i po pojedynczym teście (niezależnie od wyniku) lub przekazać obiekt, który będzie można wykorzystywany później w testach.

Rola fixture w testach

Za pomocą fixtur możemy rozwiązać:

  • Przygotowanie środowiska przed testem, modułem, klasą lub całym projektem
  • Ustawienie warunków wstępnych
  • Przywrócenie środowiska do stanu przed testem, nawet jeśli test zakończył się niepowodzeniem
  • Przekazywanie obiektu do testu
  • Sprzątanie po teście

W poniższych przykładach przedstawię jak w prosty sposób możemy ułatwić sobie automatyzację testów dzięki pytestowi.

Wykonaj powtarzalne czynności przed testem

W ten sposób możemy przygotować środowisko testowe, ustalić warunki wstępne, lub upewnić się, czy zostały poprawnie przywrócone po poprzednim teście.

Zacznijmy od stworzenia funkcji, którą następnie udekorujemy nasz test. Aby funkcja w pyteście stała się fixturem musimy dodać przed nim „@python.fixture()”

import pytest
 
@pytest.fixture()
def moj_pierwszy_fixture():
    print("Wykonuje fixture")

Teraz dodajmy ją przed wybranymi testami przy pomocy dekoratora @pytest.mark.usefixtures(), do którego wpisujemy nazwę wybranego fixtura. Można go również dodać jako argument do funkcji.

import pytest
 
@pytest.fixture()
def moj_pierwszy_fixture():
    print("Wykonuje fixture")
 
@pytest.mark.usefixtures("moj_pierwszy_fixture")
def test_pierwszy():
    print("Wykonuje test pierwszy\n")
 
def test_drugi():
    print("Wykonuje test drugi\n")
 
@pytest.mark.usefixtures("moj_pierwszy_fixture")
def test_trzeci():
    print("Wykonuje test trzeci\n")

W tym przypadku testy wykonujemy z parametrem -s, aby w konsoli były widoczne „printowane” teksty. Na poniższym wyniku przeprowadzonych „testów” widzimy, że fixtura wykonała się przed każdym z testów, gdzie została użyta, czyli przed testem pierwszym i trzecim.

pi@raspberrypi:~/tests/fixture$ pytest -s test_fixture.py
============================= test session starts ==============================
platform linux -- Python 3.5.3, pytest-4.6.3, py-1.8.0, pluggy-0.12.0
rootdir: /home/pi/tests/fixture
collected 3 items
 
test_fixture.py Wykonuje fixture
Wykonuje test pierwszy
 
.Wykonuje test drugi
 
.Wykonuje fixture
Wykonuje test trzeci
 
.
 
=========================== 3 passed in 0.36 seconds ===========================

Wyczyść po teście

Teraz zmodyfikujmy powyższą fixture, tak by po zakończeniu testu, w którym została użyta, wykonał się dodatkowy kod. Aby to zrobić musimy dodać argument request oraz zdefiniować dodatkową metodę w naszej fixturze, a następnie użyć metody request.addfinalizer()

import pytest
 
@pytest.fixture()
def moj_pierwszy_fixture(request):
    print("Wykonuje fixture")
    def na_zakonczenie():
        print("Wykonuje po zakonczonym tescie\n")
    request.addfinalizer(na_zakonczenie)
 
@pytest.mark.usefixtures("moj_pierwszy_fixture")
def test_pierwszy():
    print("Wykonuje test pierwszy")
 
def test_drugi():
    print("Wykonuje test drugi\n")
 
@pytest.mark.usefixtures("moj_pierwszy_fixture")
def test_trzeci():
    print("Wykonuje test trzeci")
    assert 1 == 2

Dodatkowo w teście „test_trzeci” dodałem asercję, która pozwoli na zakończenie testu niepowodzeniem, aby pokazać, że kod fixtury wykona się również po takim teście.


=================================== FAILURES ===================================
_________________________________ test_trzeci __________________________________
 
    @pytest.mark.usefixtures("moj_pierwszy_fixture")
    def test_trzeci():
        print("Wykonuje test trzeci")
>       assert 1==2
E       assert 1 == 2
 
test_fixture.py:20: AssertionError
====================== 1 failed, 2 passed in 1.07 seconds ======================
pi@raspberrypi:~/tests/fixture $

Dodanie obiektu do testu

Na początek stwórzmy klasę „klasa_czas()”, którą zwrócona w fixturze „czas()” jako obiekt, będzie mogła być użyta w testach. Obiekt jest dostępny dzięki użyciu fixtury jako argument testu. Niestety pytest w wersji 4.6.3 nie pozwala stworzyć fixtury bezpośrednio z klasy.

import pytest
 
class klasa_czas:
    def __init__(self):
        import time
        self.czas = time
 
    def godzina(self):
        return self.czas.strftime("%H:%M:%S")
 
    def data(self):
        return self.czas.strftime("%D")
 
 
@pytest.fixture()
def czas():
    return klasa_czas()
 
 
def test_pierwszy(czas):
    print(czas.godzina())
    print(czas.data())

Widzimy, że klasa_czas posiada dwie metody: godzina() i data(), które możemy wywołać w teście za pomocą obiektu czas.

pi@raspberrypi:~/tests/fixture $ pytest -s test_fixture.py
============================= test session starts ==============================
platform linux -- Python 3.5.3, pytest-4.6.3, py-1.8.0, pluggy-0.12.0
rootdir: /home/pi/tests/fixture
collected 1 item
 
test_fixture.py 20:18:02
07/10/19
.
 
=========================== 1 passed in 0.25 seconds ===========================
pi@raspberrypi:~/tests/fixture $

Funkcjonalność tą możesz wykorzystywać np. w przekazywaniu konfiguracji środowiska. Fixtury mają ogromne znaczenie podczas tworzenia środowiska testowego i pisania testów automatycznych. 

close

Newsletter