Как работать с фикстурами в pytest?python-62

Фикстуры в pytest — это мощный механизм для подготовки и очистки тестового окружения. Они позволяют выносить повторяющийся код из тестов и управлять тестовыми данными.

Основные понятия

  1. Фикстура (Fixture) - функция, которая подготавливает данные/состояние для теста
  2. Область видимости (Scope) - как часто фикстура создается (function, class, module, session)
  3. Зависимости - фикстуры могут зависеть от других фиктур
  4. Автоматическое использование - фикстуры можно применять автоматически

Базовый пример

import pytest

@pytest.fixture
def sample_data():
    # Эта часть выполняется перед тестом
    data = {"a": 1, "b": 2}
    yield data  # Передаем данные в тест
    # Эта часть выполняется после теста (очистка)
    print("Cleanup")

def test_sample(sample_data):
    assert sample_data["a"] == 1

Области видимости

Фикстуры могут иметь разную область видимости:

@pytest.fixture(scope="function")  # По умолчанию - создается для каждого теста
def func_scope():
    return "function"

@pytest.fixture(scope="class")  # Один раз на класс
def class_scope():
    return "class"

@pytest.fixture(scope="module")  # Один раз на модуль
def module_scope():
    return "module"

@pytest.fixture(scope="session")  # Один раз на сессию тестов
def session_scope():
    return "session"

Автоматические фикстуры

Фикстуры могут выполняться автоматически без явного вызова:

@pytest.fixture(autouse=True)
def auto_fixture():
    print("\nThis runs before each test")

def test_example():
    assert True  # auto_fixture выполнится автоматически

Фикстуры с параметрами

@pytest.fixture(params=[1, 2, 3])
def parametrized_fixture(request):
    return request.param * 2

def test_parametrized(parametrized_fixture):
    assert parametrized_fixture in [2, 4, 6]

Встроенные фикстуры

Pytest предоставляет полезные встроенные фикстуры:

  1. tmp_path - временный путь для файлов
  2. capsys - перехват stdout/stderr
  3. monkeypatch - временное изменение переменных
  4. request - информация о текущем тесте

Пример с tmp_path:

def test_create_file(tmp_path):
    file = tmp_path / "test.txt"
    file.write_text("content")
    assert file.read_text() == "content"

Фикстуры для классов

@pytest.fixture(scope="class")
def db_connection():
    conn = create_db_connection()
    yield conn
    conn.close()

@pytest.mark.usefixtures("db_connection")
class TestDatabase:
    def test_query1(self):
        assert True

    def test_query2(self):
        assert True

Окончательные фикстуры

Альтернатива yield для очистки:

@pytest.fixture
def resource(request):
    res = allocate_resource()

    def cleanup():
        res.release()

    request.addfinalizer(cleanup)
    return res

Продвинутые техники

1. Динамические фикстуры

@pytest.fixture
def dynamic_fixture(request):
    marker = request.node.get_closest_marker("data")
    if marker:
        return marker.args[0]
    return "default"

@pytest.mark.data("custom")
def test_dynamic(dynamic_fixture):
    assert dynamic_fixture == "custom"

2. Фикстуры в conftest.py

Фикстуры можно выносить в файл conftest.py для общего доступа:

# conftest.py
import pytest

@pytest.fixture
def shared_fixture():
    return "shared"

# test_file.py
def test_shared(shared_fixture):
    assert shared_fixture == "shared"

3. Фикстуры с зависимостями

@pytest.fixture
def user():
    return {"name": "Alice"}

@pytest.fixture
def active_user(user):
    user["active"] = True
    return user

def test_user(active_user):
    assert active_user["active"] is True

Лучшие практики

  1. Именуйте фикстуры понятно - db_connection, а не fixture1
  2. Используйте подходящий scope - не делайте все фикстуры session
  3. Выносите общие фикстуры в conftest.py - для переиспользования
  4. Избегайте сложной логики - фикстуры должны быть простыми
  5. Очищайте ресурсы - используйте yield или addfinalizer

Ошибки и подводные камни

  1. Изменяемые объекты - могут неожиданно меняться между тестами
  2. Слишком широкий scope - может приводить к неочевидным зависимостям
  3. Побочные эффекты - фикстуры не должны влиять друг на друга
  4. Циклические зависимости - фикстуры не должны зависеть друг от друга циклически

Резюмируем

  1. Фикстуры - мощный инструмент для подготовки тестового окружения
  2. Гибкие scope - от function до session уровня
  3. Могут зависеть друг от друга и параметризоваться
  4. Автоматическое выполнение - через autouse=True
  5. Лучше выносить общие фикстуры в conftest.py

Правильное использование фикстур делает тесты:

  • Более чистыми
  • Менее связанными
  • Более поддерживаемыми
  • Более эффективными

Начните с простых фикстур и постепенно осваивайте более сложные возможности.