pyTest: fixtures

pyTest: stwórz własny fixture #1

Poziom trudności
2/5

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ć:

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()”

  1. import pytest
  2. @pytest.fixture()
  3. def moj_pierwszy_fixture():
  4. 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.

  1. import pytest
  2. @pytest.fixture()
  3. def moj_pierwszy_fixture():
  4. print("Wykonuje fixture")
  5. @pytest.mark.usefixtures("moj_pierwszy_fixture")
  6. def test_pierwszy():
  7. print("Wykonuje test pierwszy\n")
  8. def test_drugi():
  9. print("Wykonuje test drugi\n")
  10. @pytest.mark.usefixtures("moj_pierwszy_fixture")
  11. def test_trzeci():
  12. 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()

  1. import pytest
  2. @pytest.fixture()
  3. def moj_pierwszy_fixture(request):
  4. print("Wykonuje fixture")
  5. def na_zakonczenie():
  6. print("Wykonuje po zakonczonym tescie\n")
  7. request.addfinalizer(na_zakonczenie)
  8. @pytest.mark.usefixtures("moj_pierwszy_fixture")
  9. def test_pierwszy():
  10. print("Wykonuje test pierwszy")
  11. def test_drugi():
  12. print("Wykonuje test drugi\n")
  13. @pytest.mark.usefixtures("moj_pierwszy_fixture")
  14. def test_trzeci():
  15. print("Wykonuje test trzeci")
  16. 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.

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 po zakonczonym tescie
Wykonuje test drugi
.Wykonuje fixture
Wykonuje test trzeci
FWykonuje po zakonczonym tescie
=================================== 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.

  1. import pytest
  2. class klasa_czas:
  3. def __init__(self):
  4. import time
  5. self.czas = time
  6. def godzina(self):
  7. return self.czas.strftime("%H:%M:%S")
  8. def data(self):
  9. return self.czas.strftime("%D")
  10. @pytest.fixture()
  11. def czas():
  12. return klasa_czas()
  13. def test_pierwszy(czas):
  14. print(czas.godzina())
  15. 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ść ta może być wykorzystywana np. w przekazywaniu konfiguracji środowiska. Fixtury mają ogromne znaczenie podczas tworzenia środowiska testowego i pisania testów automatycznych. 

Dodaj komentarz