Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. conftest.py: переиспользование фикстур
conftest_py

conftest.py: переиспользование фикстур

Общие фикстуры в conftest.py, scope, автоиспользование. DRY для тестов

У вас 5 файлов тестов. В каждом дублируется фикстура client для HTTP-клиента. Как вынести её в одно место?

Цель: Научиться использовать conftest.py для переиспользования фикстур.

Вы точно готовы?

Убедитесь, что умеете:

# Создавать фикстуры @pytest.fixture def database(): db = connect_to_db() yield db db.close() # Использовать фикстуры def test_user(database): user = database.get_user(1) assert user.name == "Alice"

Если фикстуры непонятны — вернитесь к pytest-basics урок 6.

Проблема: дублирование фикстур

Без conftest.py

# tests/test_api.py @pytest.fixture def client(): return APIClient(base_url="https://api.example.com") def test_get_users(client): users = client.get("/users") assert len(users) > 0 # tests/test_auth.py @pytest.fixture # ❌ Дублирование! def client(): return APIClient(base_url="https://api.example.com") def test_login(client): result = client.post("/login", {"user": "alice"}) assert result["token"] is not None # tests/test_products.py @pytest.fixture # ❌ Ещё раз дублирование! def client(): return APIClient(base_url="https://api.example.com") def test_get_products(client): products = client.get("/products") assert len(products) > 0

Проблемы:

  • ❌ Фикстура client скопирована в 3 файлах
  • ❌ Если изменится base_url — нужно менять в 3 местах
  • ❌ Нарушение DRY (Don't Repeat Yourself)

Решение: conftest.py

Создание conftest.py

# tests/conftest.py import pytest from src.api_client import APIClient @pytest.fixture def client(): """HTTP-клиент для API""" return APIClient(base_url="https://api.example.com") @pytest.fixture def database(): """Подключение к БД""" db = connect_to_db() yield db db.close()

Теперь все тесты могут использовать эти фикстуры:

# tests/test_api.py def test_get_users(client): # ✅ Фикстура из conftest.py users = client.get("/users") assert len(users) > 0 # tests/test_auth.py def test_login(client): # ✅ Та же фикстура result = client.post("/login", {"user": "alice"}) assert result["token"] is not None

✅ Фикстура определена ОДИН раз, используется ВЕЗДЕ!

Как работает conftest.py

Pytest автоматически:

  1. Находит conftest.py в tests/
  2. Загружает все фикстуры из него
  3. Делает их доступными для ВСЕХ тестов

Не нужно:

  • ❌ Импортировать conftest.py
  • ❌ Указывать путь к нему

✅ Pytest находит его автоматически!

Множественные фикстуры

# tests/conftest.py import pytest from unittest.mock import Mock @pytest.fixture def client(): """API-клиент""" return APIClient(base_url="https://api.example.com") @pytest.fixture def mock_requests(): """Мок для requests""" import requests_mock with requests_mock.Mocker() as m: yield m @pytest.fixture def test_user(): """Тестовый пользователь""" return { "id": 1, "name": "Alice", "email": "alice@example.com" } @pytest.fixture def test_product(): """Тестовый продукт""" return { "id": 101, "name": "Laptop", "price": 1000 }

Использование:

# tests/test_orders.py def test_create_order(test_user, test_product): """Используем обе фикстуры""" order = create_order(user=test_user, product=test_product) assert order.total == 1000

autouse: автоматическое применение

Проблема: setup нужен в КАЖДОМ тесте

# Хотим чтобы перед каждым тестом: # 1. Очищалась БД # 2. Применялись migrations # 3. Создавались тестовые данные

Решение: autouse=True

# tests/conftest.py @pytest.fixture(autouse=True) def setup_database(): """Выполняется ПЕРЕД каждым тестом автоматически""" # Setup db = connect_to_db() db.drop_all_tables() db.create_tables() db.apply_migrations() yield # Teardown db.drop_all_tables() db.close()

Теперь:

# tests/test_users.py def test_create_user(): # setup_database выполнился АВТОМАТИЧЕСКИ! user = User.create(name="Alice") assert user.id is not None

✅ Не нужно указывать setup_database в параметрах!

Когда использовать autouse

✅ Хорошие случаи:

  • Очистка БД перед каждым тестом
  • Настройка логирования
  • Установка переменных окружения
  • Инициализация кешей

❌ Плохие случаи:

  • Медленные операции (БД для ВСЕХ тестов)
  • Фикстуры нужные только некоторым тестам

Пример: logging setup

@pytest.fixture(autouse=True, scope="session") def setup_logging(): """Настройка логирования один раз для всей сессии""" import logging logging.basicConfig(level=logging.INFO)

Иерархия conftest.py

Множественные conftest.py

tests/
├── conftest.py          # Глобальные фикстуры (для ВСЕХ тестов)
├── test_root.py
├── api/
│   ├── conftest.py      # Фикстуры для API-тестов
│   ├── test_users.py
│   └── test_products.py
└── unit/
    ├── conftest.py      # Фикстуры для unit-тестов
    ├── test_calculator.py
    └── test_utils.py

Как работает:

  1. tests/conftest.py — фикстуры доступны ВЕЗДЕ
  2. tests/api/conftest.py — дополнительные фикстуры только для tests/api/*
  3. tests/unit/conftest.py — дополнительные фикстуры только для tests/unit/*

Практический пример

# tests/conftest.py (глобальные) @pytest.fixture def database(): """БД для всех тестов""" return connect_to_db() # tests/api/conftest.py (только для API-тестов) @pytest.fixture def api_client(database): """API-клиент с доступом к БД""" return APIClient(db=database) @pytest.fixture def auth_headers(): """Заголовки аутентификации""" return {"Authorization": "Bearer fake-token"} # tests/unit/conftest.py (только для unit-тестов) @pytest.fixture def mock_database(): """Мок БД для unit-тестов""" from unittest.mock import Mock return Mock()

Использование:

# tests/api/test_users.py def test_get_users(api_client, auth_headers): # ✅ api_client из tests/api/conftest.py # ✅ Может использовать database из tests/conftest.py response = api_client.get("/users", headers=auth_headers) assert response.status_code == 200 # tests/unit/test_calculator.py def test_add(): # ✅ Не видит api_client (он только для tests/api/) # ✅ Может использовать mock_database из tests/unit/conftest.py assert add(2, 3) == 5

Порядок загрузки

Pytest загружает conftest.py от корня к листьям:

1. tests/conftest.py
2. tests/api/conftest.py
3. Тест: tests/api/test_users.py

✅ Фикстуры ближе к тесту могут переопределить глобальные!

# tests/conftest.py @pytest.fixture def base_url(): return "https://api.example.com" # tests/api/conftest.py @pytest.fixture def base_url(): return "https://api.test.com" # Переопределяет глобальную! # tests/api/test_users.py def test_users(base_url): print(base_url) # https://api.test.com ✅

Scope и conftest.py

Scope для производительности

# tests/conftest.py @pytest.fixture(scope="session") def database(): """БД создаётся ОДИН раз для всей сессии""" print("🔌 Connecting to database...") db = connect_to_db() yield db print("🧹 Closing database...") db.close() @pytest.fixture(scope="module") def api_client(database): """API-клиент создаётся один раз для каждого модуля""" print("🚀 Creating API client...") return APIClient(db=database) @pytest.fixture(scope="function") def test_user(): """Новый пользователь для КАЖДОГО теста""" print("👤 Creating test user...") return User(name="TestUser")

Запускаем 3 теста в одном файле:

🔌 Connecting to database...     # session (один раз)
🚀 Creating API client...        # module (один раз на файл)
👤 Creating test user...         # function (каждый тест)
test 1 PASSED
👤 Creating test user...
test 2 PASSED
👤 Creating test user...
test 3 PASSED
🧹 Closing database...           # session teardown

✅ База создаётся ОДИН раз, пользователь — для каждого теста!

Рекомендации по scope

ScopeКогда использоватьПример
functionЧистое состояние для каждого теста (default)test_user, test_product
classТесты в одном классеДля Test классов
moduleМедленный setup для одного файлаAPI client, mock server
sessionОчень медленный setup (один раз)Database, Docker container

Практический пример: реальный проект

# tests/conftest.py (глобальный) import pytest from src.database import Database from src.cache import RedisCache @pytest.fixture(scope="session") def database(): """PostgreSQL БД (один раз для всех тестов)""" db = Database("postgresql://localhost/test_db") db.create_tables() yield db db.drop_tables() db.close() @pytest.fixture(scope="session") def redis(): """Redis кеш (один раз для всех тестов)""" cache = RedisCache(host="localhost", port=6379, db=1) yield cache cache.flushdb() cache.close() @pytest.fixture(autouse=True) def clean_database(database): """Очищаем БД перед каждым тестом""" database.truncate_all_tables() # tests/api/conftest.py @pytest.fixture def api_client(database, redis): """API-клиент с БД и кешем""" from src.api import APIClient return APIClient(db=database, cache=redis) @pytest.fixture def auth_token(): """JWT токен для аутентификации""" from src.auth import create_token return create_token(user_id=1, role="admin") # tests/unit/conftest.py @pytest.fixture def mock_database(): """Мок БД для unit-тестов""" from unittest.mock import Mock mock_db = Mock() mock_db.get_user.return_value = {"id": 1, "name": "Alice"} return mock_db

Использование:

# tests/api/test_users.py def test_create_user(api_client, auth_token): """Integration-тест с реальной БД""" response = api_client.post( "/users", json={"name": "Bob"}, headers={"Authorization": f"Bearer {auth_token}"} ) assert response.status_code == 201 # tests/unit/test_user_service.py def test_get_user(mock_database): """Unit-тест с моком""" from src.services import UserService service = UserService(db=mock_database) user = service.get_user(1) assert user["name"] == "Alice" mock_database.get_user.assert_called_once_with(1)

Что вы изучили

  • conftest.py — общие фикстуры для всех тестов
  • autouse=True — автоматическое применение фикстур
  • Иерархия — множественные conftest.py в поддиректориях
  • Scope — session/module/function для производительности
  • Переопределение — локальные фикстуры заменяют глобальные
  • Организация — разделение по типам тестов (api/unit)

Проверьте свои знания

Вопросы ещё не добавлены

Вопросы для этой подтемы ещё не добавлены.

Далее: Мини-проект: Todo с кешем и API