# pytest

> Самый популярный фреймворк тестирования для Python — простые тесты на обычных assert, фикстуры, параметризация и большая экосистема плагинов.

Источник: https://llmdocs.ru/wiki/pytest/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Тип**: фреймворк тестирования
- **Автор**: Хольгер Крекель
- **Появился**: 2004 (как py.test)
- **Лицензия**: MIT
- **Ключевые возможности**: assert с подробными отчётами, фикстуры, параметризация, маркеры
- **Плагины**: pytest-cov, pytest-xdist, pytest-asyncio, pytest-mock, hypothesis
- **Альтернатива**: unittest (стандартная библиотека)
**pytest** — фреймворк для написания и запуска тестов на [Python](https://llmdocs.ru/wiki/python.md), самый популярный в экосистеме. Тест в pytest — обычная функция, имя которой начинается с `test_`, а проверки — обычный оператор `assert`; при падении pytest показывает подробности: какие значения сравнивались и чем они отличаются. Мощь фреймворка — в **фикстурах** для подготовки окружения, **параметризации** для прогона одного теста на многих наборах данных и сотнях плагинов.

pytest используют и для модульных тестов, и для интеграционных, и для проверки API, баз данных, веб-интерфейсов; он запускает и тесты, написанные на стандартном `unittest`.

## История

Хольгер Крекель начал разработку в 2004 году в рамках проекта PyPy под названием `py.test` — частью библиотеки `py`. Позже pytest выделился в самостоятельный проект, команда запуска стала называться `pytest`, а сам фреймворк вытеснил `unittest` в большинстве новых проектов. Сейчас pytest развивает сообщество под организацией pytest-dev, распространяется под лицензией MIT.[1]

## Первые тесты

```python
# test_pricing.py
import pytest
from shop.pricing import apply_discount

def test_discount_reduces_price():
    assert apply_discount(1000, percent=10) == 900

def test_negative_percent_rejected():
    with pytest.raises(ValueError, match="percent"):
        apply_discount(1000, percent=-5)
```

```bash
uv add --dev pytest
uv run pytest                 # найдёт файлы test_*.py и функции test_*
uv run pytest -x -q           # остановиться на первой ошибке, краткий вывод
uv run pytest -k discount     # только тесты, в имени которых есть «discount»
uv run pytest --lf            # перезапустить упавшие в прошлый раз
```

pytest переписывает `assert` при импорте тестов, поэтому сообщение об ошибке показывает обе стороны сравнения, а для списков, словарей и строк — построчную разницу. Не нужно запоминать десятки методов вроде `assertEqual`, `assertIn`, `assertDictEqual`, как в unittest.[4][5]

## Фикстуры

Фикстура — функция, которая готовит ресурс для теста и при необходимости убирает его после. Тест получает фикстуру, просто указав её имя в параметрах:[2]

```python
import pytest

@pytest.fixture
def db(tmp_path):
    conn = connect(tmp_path / "test.sqlite")   # tmp_path — встроенная фикстура: временный каталог
    conn.migrate()
    yield conn                                 # всё до yield — подготовка, после — очистка
    conn.close()

@pytest.fixture
def user(db):                                  # фикстуры могут зависеть от других фикстур
    return db.create_user(email="alice@example.com")

def test_user_can_place_order(db, user):
    order = db.create_order(user_id=user.id, total=500)
    assert order.status == "new"
```

- **Область действия** (`scope="function" | "module" | "session"`) задаёт, как часто создаётся фикстура: например, одна база данных на весь прогон и транзакция с откатом на каждый тест.
- **`conftest.py`** — файл с общими фикстурами для каталога и вложенных каталогов, без импортов в тестах.
- **Встроенные фикстуры**: `tmp_path` (временный каталог), `monkeypatch` (подмена атрибутов и [переменных окружения](https://llmdocs.ru/wiki/environment-variables.md)), `capsys` (перехват вывода), `caplog` (логи).

## Параметризация

```python
@pytest.mark.parametrize(
    ("price", "percent", "expected"),
    [
        (1000, 0, 1000),
        (1000, 10, 900),
        (999, 33, 669),
        pytest.param(1000, 100, 0, id="full-discount"),
    ],
)
def test_apply_discount(price, percent, expected):
    assert apply_discount(price, percent=percent) == expected
```

Каждый набор становится отдельным тестом с собственным именем в отчёте, поэтому видно, какие именно случаи упали.[3]

## Маркеры и конфигурация

Маркеры помечают тесты: `@pytest.mark.skip`, `@pytest.mark.skipif(sys.platform == "win32", reason=...)`, `@pytest.mark.xfail` для известных ошибок и пользовательские метки вроде `slow` или `integration`, чтобы запускать группы тестов (`pytest -m "not slow"`). Настройки хранятся в [pyproject.toml](https://llmdocs.ru/wiki/pyproject-toml.md):

```toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra --strict-markers"
markers = ["slow: долгие тесты", "integration: требуют внешних сервисов"]
```

## Плагины

| Плагин | Назначение |
|---|---|
| pytest-cov | покрытие кода тестами |
| pytest-xdist | параллельный запуск на нескольких ядрах (`-n auto`)[6] |
| pytest-asyncio, anyio | тесты асинхронного кода ([asyncio](https://llmdocs.ru/wiki/asyncio.md)) |
| pytest-mock | удобная обёртка над `unittest.mock` |
| hypothesis | тестирование на основе свойств: генерирует сотни случайных входов и находит граничные случаи |
| pytest-django, pytest-httpx | интеграция с фреймворками и HTTP-клиентами |

## Практика

- Тесты запускаются в [CI/CD](https://llmdocs.ru/wiki/ci-cd.md) при каждом pull request; быстрые модульные тесты — отдельно от медленных интеграционных.
- Один тест — одно поведение; имя теста описывает ожидаемый результат.
- Внешние сервисы в модульных тестах подменяют, а интеграционные тесты гоняют против настоящих зависимостей, например в контейнерах [Docker](https://llmdocs.ru/wiki/docker.md).
- Покрытие — ориентир, а не цель: 100% строк не гарантируют проверку важных случаев.

## См. также

- [Python](https://llmdocs.ru/wiki/python.md)
- [CI/CD](https://llmdocs.ru/wiki/ci-cd.md)
- [pyproject.toml](https://llmdocs.ru/wiki/pyproject-toml.md)
- [Декораторы](https://llmdocs.ru/wiki/decorators.md)
- [Ruff](https://llmdocs.ru/wiki/ruff.md)

## Источники

1. [pytest — документация](https://docs.pytest.org/en/stable/)
2. [pytest — How to use fixtures](https://docs.pytest.org/en/stable/how-to/fixtures.html)
3. [pytest — How to parametrize fixtures and test functions](https://docs.pytest.org/en/stable/how-to/parametrize.html)
4. [pytest — How to write and report assertions in tests](https://docs.pytest.org/en/stable/how-to/assert.html)
5. [Python — unittest](https://docs.python.org/3/library/unittest.html)
6. [pytest-xdist — документация](https://pytest-xdist.readthedocs.io/)
