Когда выбирать pytest
Подходит
- Любой проект на Python: от юнит-тестов до API и UI-автоматизации.
- Нужна параметризация — один тест на десятки наборов данных.
- Нужно переиспользовать подготовку окружения через фикстуры с разной областью действия.
- Нужна интеграция с Allure, Playwright, Selenium, requests и CI.
Не подходит
- Проект на Java или TypeScript — используйте JUnit 5 или Vitest.
- Нужен только нагрузочный прогон — берите Locust или k6.
Установка
pip install pytestПримеры тестов
Первый тест
1def add(a: int, b: int) -> int:
2 return a + b
3
4
5def test_add_positive_numbers():
6 assert add(2, 3) == 5
7
8
9def test_add_negative_numbers():
10 assert add(-2, -3) == -5Запуск: pytest -v. Файлы и функции с префиксом test_ находятся автоматически, а при падении assert показывает значения обеих сторон.
Фикстура для HTTP-клиента
1import pytest
2import requests
3
4
5@pytest.fixture(scope="session")
6def api():
7 session = requests.Session()
8 session.headers.update({"Accept": "application/json"})
9 session.base_url = "https://reqres.in/api"
10 yield session
11 session.close()
12
13
14def test_get_user(api):
15 response = api.get(f"{api.base_url}/users/2")
16
17 assert response.status_code == 200
18 assert response.json()["data"]["id"] == 2Код до yield — подготовка, после — очистка. scope="session" создаёт клиент один раз на весь прогон.
Параметризация
1import pytest
2
3
4def is_valid_password(value: str) -> bool:
5 return len(value) >= 8 and any(c.isdigit() for c in value)
6
7
8@pytest.mark.parametrize(
9 "password, expected",
10 [
11 ("abc12345", True),
12 ("short1", False),
13 ("nodigitshere", False),
14 ("", False),
15 ],
16)
17def test_password_validation(password, expected):
18 assert is_valid_password(password) is expectedКаждая строка становится отдельным тестом в отчёте — видно, какой именно набор данных упал.
Типичные ошибки
- Фикстуры с побочными эффектами без очистки
- Созданные в тесте данные остаются в базе и ломают следующие запуски. Используйте yield и удаляйте всё в блоке после него.
- Слишком большая область scope
- scope="session" ускоряет прогон, но общий изменяемый объект делает тесты зависимыми. Для изменяемого состояния оставляйте function.
- Логика внутри тестов
- Циклы и условия в теле теста скрывают, что именно проверяется. Выносите варианты в parametrize, а не в for.
- Незарегистрированные маркеры
- Опечатка в @pytest.mark.smoke молча создаёт новый маркер. Зарегистрируйте маркеры в pytest.ini и включите --strict-markers.
Вопросы на собеседовании по pytest
- Что такое фикстура и чем scope="function" отличается от scope="session"?
- Как работает parametrize и когда лучше использовать его вместо цикла?
- Где хранить общие фикстуры и как их находит pytest? (conftest.py)
- Как запустить только часть тестов? (-k, -m, путь к файлу)
Отработать ответы можно на мок-собеседовании.
Частые вопросы
pytest или unittest — что выбрать?
Для новых проектов чаще выбирают pytest: короче синтаксис, обычный assert, фикстуры и параметризация без классов. unittest из стандартной библиотеки уместен, если нельзя ставить сторонние зависимости; при этом pytest умеет запускать и unittest-тесты.
Как получить красивый отчёт в pytest?
Подключите плагин allure-pytest и запускайте pytest --alluredir=allure-results. Затем allure serve allure-results откроет интерактивный отчёт с шагами и вложениями.
Можно ли писать UI-тесты на pytest?
Да. pytest используется как тест-раннер для Playwright (pytest-playwright), Selenium и Selene. Фикстуры управляют жизненным циклом браузера.
Как запускать тесты параллельно?
Установите pytest-xdist и запустите pytest -n auto. Тесты должны быть независимыми: без общего изменяемого состояния и без зависимости от порядка.