pyTest Tutorial #8: Markery personalizowane
W ósmej odsłonie serii pyTest Tutorial, jak tworzyć własne, spersonalizowane markery, po co je rejestrować ora jak ich używać.
W ósmej części serii pyTest Tutorial omówię czym są oraz jak wykonać markery tworzone przez nas, czyli tak zwane markery personalizowane lub custom’owe. Zagadnienie nie jest rozbudowane, ale warto je znać.
Personalizowane markery
Jak wspomniałem na wstępie, pyTest umożliwia nam tworzenie własnych markerów. Służą one głównie do grupowania testcase’ów, poprzez umieszczenie ich przed wybranymi testami. Dzięki temu w łatwy sposób możemy uruchomić dowolne kolekcje testów. Przykładowymi nazwami markerów mogą być regression, smoke, calculation_module – oczywiście możecie wymyślać swoje nazwy. Weźmy jako przykład plik z testami test_calc.py, w którym mamy następujące testy:
test_addition
test_addition_10_5
test_subtraction_13_21
test_addition_2_m4
test_addition_m10_12
test_subtraction_m3_m7
test_multiplication_2_2
test_multiplication_m7_3
test_division_10_2
test_division_12_0
Oznaczmy wszystkie testy, związane z wykonywanie dodawania, poniższym markerem:
@pytest.mark.addition
Przejdźmy więc do uruchomienia testów z markerem addition, wykorzystując do tego parametr -m:
qabrio@test:~/basic_calculator$ pytest -vr x test_calc.py -m addition
================================================ test session starts ================================================
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- /home/qabrio/basic_calculator/env/bin/python3.6
cachedir: .cache
rootdir: /home/qabrio/basic_calculator, inifile:
collected 12 items
test_calc.py::test_addition[10-5] PASSED [ 16%]
test_calc.py::test_addition[2--4] PASSED [ 33%]
test_calc.py::test_addition[-10-12] PASSED [ 50%]
test_calc.py::test_addition_10_5 PASSED [ 66%]
test_calc.py::test_addition_2_m4 PASSED [ 83%]
test_calc.py::test_addition_m10_12 PASSED [100%]
================================================ 6 tests deselected =================================================
====================================== 6 passed, 6 deselected in 0.01 seconds =======================================
qabrio@test:~/basic_calculator$
Zgodnie z tym, co się spodziewaliśmy, uruchomiły się wszystkie testy związane z dodawaniem. Używając markerów, warto wiedzieć, że argument -m może przyjmować nie tylko same markery, ale także wyrażenia logiczne zawierające not, and lub or. Dzięki czemu możemy łączyć ze sobą kilka markerów, wydzielając tym samym kolejne podzbiory. Najprostszym przykładem takiego użycia jest wyrażenie ‚not addition’. Spowoduje to działanie przeciwstawne do zaprezentowanego w poprzednim przykładzie, czyli uruchomienie wszystkich testów, które nie mają markera addition.
Warto również wiedzieć, że do wybierania testów można także wykorzystywać markery wbudowane, takie jak skip, skipif, xfail, czy parametrize. Oczywiście wybierając testy oznaczone markerem skip, żaden z nich się nie wykona. Jest to spowodowane faktem, że w dalszym ciągu marker ten będzie sprawował swoją wbudowaną funkcję. Dostaniemy tylko informacje o liczbie testów, które są faktycznie oznaczone tym markerem.
Sprawdźmy więc na przykładzie jak wykorzystać wbudowane markery na przykładzie. W tym celu wybierzmy testy, które mają marker addition, a jednocześnie nie posiadają parametryzacji. Można to wykonać jak poniżej:
qabrio@test:~/basic_calculator$ pytest -vr x test_calc.py -m 'addition and not parametrize'
================================================ test session starts ================================================
platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- /home/qabrio/basic_calculator/env/bin/python3.6
cachedir: .cache
rootdir: /home/qabrio/basic_calculator, inifile:
collected 12 items
test_calc.py::test_addition_10_5 PASSED [ 33%]
test_calc.py::test_addition_2_m4 PASSED [ 66%]
test_calc.py::test_addition_m10_12 PASSED [100%]
================================================ 9 tests deselected =================================================
====================================== 3 passed, 9 deselected in 0.01 seconds =======================================
qabrio@test:~/basic_calculator$
Podczas tworzenia wyrażenia z markerami, należy jednak pamiętać, żeby całe wyrażenie znajdowało się w cudzysłowie lub apostrofach. Jeśli tego nie zrobimy, pierwsze słowo po -m będzie oznaczało żądany marker, a kolejne pytest będzie próbował odczytać jako pliki z testami.
Rejestrowanie markerów
Wcześniejsze przykłady używania markerów, pokazywały użycie markerów nierejestrowanych. Zatem czym są markery nierejstrowane. Są to markery, które nie zostały ujęte w spisie markerów. Spis ten możemy uzyskać poprzez użycie komendy pytest z flagą –markers:
qabrio@test:~/basic_calculator$ pytest --markers
@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/latest/warnings.html#pytest-mark-filterwarnings
@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see https://docs.pytest.org/en/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/latest/parametrize.html for more info and examples.
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/latest/fixture.html#usefixtures
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
Jeśli na przykład pracujemy w dużym projekcie i chcemy aby pozostali testerzy wiedzieli do czego służą dane markery i po co zostały ustalone, warto skorzystać z możliwości rejestracji markerów. Można to wykonać poprzez edycję pliku pytest.ini, a w przypadku gdy jeszcze nie mamy tego pliku – poprzez jego stworzenie. Jeśli chcemy dodać marker addition, plik ten może wyglądać w następujący sposób:
[pytest]
markers=
addition: test verify working of addition calculation
smoke: short set of initial tests
Każdy kolejny marker znajduje się w sekcji markers, gdzie kolejny nowy marker jest w następnym wierszu. Nazwa markera od jego opisu jest oddzielona dwukropkiem. Teraz po sprawdzeniu dostępnych markerów, zobaczymy także te dodane przez nas:
qabrio@test:~/basic_calculator$ pytest --markers
@pytest.mark.addition: test verify working of addition calculation
@pytest.mark.smoke: short set of initial tests
(...)
Tworzenie własnych markerów jest czynnością prostą, a używanie ich niesie sporo korzyści. Dzięki nim możemy w skuteczny sposób pogrupować testy, co wpływa pozytywnie na zarządzanie nimi, a przy tym uruchamianie testów staje się niezwykle łatwe, gdyż nie trzeba znać pełnej ścieżki do konkretnych testów. Ponadto warto korzystać z mechanizmu rejestrowania markerów, gdyż można wtedy podejrzeć co one oznaczają.