Python · Тестування з pytest · Середній
Маркери та вибір тестів
Позначайте тести вбудованими та власними маркерами для контролю запуску.
Короткий вступ до теми та пояснення перед вправами (вправи нижче):
Мітки на практиці
#Довідник: мітки
#Вправи:
Пропуск тесту з причиною
#Напишіть тест-функцію `test_future_feature`, яка тестуватиме якусь ще не реалізовану функцію. Позначте її `@pytest.mark.skip` з описовим рядком `reason`. Запустіть `pytest -v` і переконайтеся, що тест відображається як `SKIPPED` з видимою причиною. Потім запустіть `pytest -v -rs`, щоб побачити причину пропуску в розділі підсумку.
import pytest
@pytest.mark.skip(reason='...') # вкажіть вашу причину тут
def test_future_feature():
# цей код ніколи не виконується при пропуску
result = some_unimplemented_function()
assert result == 42
Рішення
import pytest
@pytest.mark.skip(reason='відшкодування платежів ще не реалізовано -- дивіться issue #42')
def test_future_feature():
result = some_unimplemented_function()
assert result == 42
Умовний пропуск за версією Python
#Напишіть тест, що використовує `@pytest.mark.skipif` для пропуску, якщо версія Python нижче 3.10. У тілі тесту використайте синтаксис оператора `match` (доданого у Python 3.10): `match value: case 1: result = "one"`. Запустіть `pytest -v` -- тест має виконуватися на Python 3.10+ і пропускатися на більш ранніх версіях. Виведіть `sys.version` у тесті, щоб підтвердити, яка версія його запустила.
import sys
import pytest
@pytest.mark.skipif(
# умова: sys.version_info < (?, ?),
# reason='...',
)
def test_match_statement():
value = 1
# використайте тут оператор match/case
# assert result == 'one'
Рішення
import sys
import pytest
@pytest.mark.skipif(
sys.version_info < (3, 10),
reason='оператор match доданий у Python 3.10',
)
def test_match_statement():
print(f'запускається на Python {sys.version}')
value = 1
match value:
case 1:
result = 'one'
case _:
result = 'other'
assert result == 'one'
Позначення тесту як очікуваного збою (xfail)
#Напишіть два тести: `test_known_bug` і `test_known_bug_strict`. Обидва викликають `int("abc")` без try/except (це підіймає `ValueError`). Позначте `test_known_bug` через `@pytest.mark.xfail`, а `test_known_bug_strict` -- через `@pytest.mark.xfail(strict=True)`. Запустіть `pytest -v` -- обидва мають відображатися як `XFAIL`. Зверніть увагу на різницю: без `strict` неочікуваний успіх відобразиться як `XPASS` (попередження, але не збій); з `strict=True` неочікуваний успіх стає жорстким `FAILED`, що блокує CI.
import pytest
@pytest.mark.xfail(reason=...) # вкажіть причину
def test_known_bug():
result = int('abc')
assert result == 0
@pytest.mark.xfail(strict=..., reason=...) # додайте параметр strict=
def test_known_bug_strict():
result = int('abc')
assert result == 0
Рішення
import pytest
@pytest.mark.xfail(reason='баг #77: int() не обробляє літери')
def test_known_bug():
result = int('abc')
assert result == 0
# З strict=True: якщо цей тест несподівано пройде, pytest повідомить про FAILED
@pytest.mark.xfail(strict=True, reason='баг #77')
def test_known_bug_strict():
result = int('abc')
assert result == 0
Реєстрація власної мітки та фільтрація через -m
#Додайте `slow` до секції `markers` у `pytest.ini`. Напишіть три тест-функції в одному файлі: `test_fast_calculation` (без мітки, просто `assert 2 + 2 == 4`), `test_slow_api_call` і `test_slow_db_query` (обидві позначені `@pytest.mark.slow`). Запустіть `pytest -m slow -v` і переконайтеся, що виконуються лише два повільні тести. Запустіть `pytest -m "not slow" -v` і переконайтеся, що виконується лише швидкий тест.
# pytest.ini
# [pytest]
# markers =
# slow: позначає тести як повільні (відмінити з '-m "not slow"')
# test_mixed.py
import pytest
def test_fast_calculation():
assert 2 + 2 == 4
@pytest.mark.slow
def test_slow_api_call():
# симуляція повільної операції
import time
time.sleep(0.1)
assert True
@pytest.mark.slow
def test_slow_db_query():
import time
time.sleep(0.1)
assert True
Рішення
# pytest.ini
# [pytest]
# markers =
# slow: позначає тести як повільні (відмінити з '-m "not slow"')
# test_mixed.py
import pytest
def test_fast_calculation():
assert 2 + 2 == 4
@pytest.mark.slow
def test_slow_api_call():
import time
time.sleep(0.1)
assert True
@pytest.mark.slow
def test_slow_db_query():
import time
time.sleep(0.1)
assert True
Поєднання кількох міток
#Зареєструйте обидві мітки `slow` і `unix_only` у `pytest.ini`. Напишіть `test_fast_check` без міток. Напишіть `test_file_permissions` з мітками `@pytest.mark.slow`, `@pytest.mark.unix_only` ТА `@pytest.mark.skipif(sys.platform == 'win32', ...)`. Запустіть `pytest -v`, щоб переконатися, що обидва тести виконуються (або пропускаються на Windows). Потім запустіть `pytest -m slow -v` -- вибирається лише повільний тест. Потім запустіть `pytest -m unix_only -v` -- той самий результат: вибирається лише тест платформи. Зверніть увагу, що `@pytest.mark.skipif` забезпечує захист під час виконання, а мітка вмикає фільтрацію через CLI.
# pytest.ini
# [pytest]
# markers =
# slow: повільні тести
# unix_only: запускається лише на Unix/Linux/macOS
import sys
import pytest
def test_fast_check():
assert 'py' in 'pytest'
@pytest.mark.slow
@pytest.mark.unix_only
@pytest.mark.skipif(sys.platform == 'win32', reason='використовує права доступу POSIX')
def test_file_permissions():
import os
import tempfile
with tempfile.NamedTemporaryFile() as f:
os.chmod(f.name, 0o644)
mode = oct(os.stat(f.name).st_mode)[-3:]
assert mode == '644'
Рішення
# pytest.ini
# [pytest]
# markers =
# slow: повільні тести
# unix_only: запускається лише на Unix/Linux/macOS
import sys
import pytest
def test_fast_check():
assert 'py' in 'pytest'
@pytest.mark.slow
@pytest.mark.unix_only
@pytest.mark.skipif(sys.platform == 'win32', reason='використовує права доступу POSIX')
def test_file_permissions():
import os
import tempfile
with tempfile.NamedTemporaryFile() as f:
os.chmod(f.name, 0o644)
mode = oct(os.stat(f.name).st_mode)[-3:]
assert mode == '644'