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

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

@potapov_me

Платформа

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

Контент

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

Компания

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

Аккаунт

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

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

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Продвинутые фикстуры: scope и autouse
advanced_fixtures_scope

Продвинутые фикстуры: scope и autouse

Session/module/function scope, autouse для автоматических фикстур, стратегии оптимизации

У вас 100 тестов. Каждый создаёт БД соединение. Setup занимает 2 секунды. Итого 200 секунд только на setup! Как оптимизировать?

Цель: Освоить продвинутые техники работы с фикстурами для максимальной производительности.

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

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

# Базовые фикстуры @pytest.fixture def database(): db = connect_to_db() yield db db.close() def test_query(database): result = database.query("SELECT * FROM users") assert len(result) > 0

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

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

Код с дорогим setup

@pytest.fixture def database(): """Подключение к БД — медленно!""" print("🔌 Connecting to database... (2 seconds)") db = connect_to_db() # 2 секунды yield db print("🧹 Closing connection...") db.close() def test_user_1(database): assert database.query("SELECT * FROM users") is not None def test_user_2(database): assert database.query("SELECT count(*) FROM users") > 0 # ... ещё 98 тестов

Запуск:

pytest tests/test_database.py -v

Результат:

🔌 Connecting... (2s)
test_user_1 PASSED
🧹 Closing...

🔌 Connecting... (2s)
test_user_2 PASSED
🧹 Closing...

# ... повторяется 100 раз

❌ 200 секунд только на setup!

Решение: scope оптимизация

scope="session" — один раз для всех тестов

@pytest.fixture(scope="session") def database(): """Создаётся ОДИН раз для всей сессии""" print("🔌 Connecting to database...") db = connect_to_db() yield db print("🧹 Closing connection...") db.close()

Запуск 100 тестов:

🔌 Connecting...  (1 раз!)
test_user_1 PASSED
test_user_2 PASSED
...
test_user_100 PASSED
🧹 Closing...     (1 раз!)

==================== 100 passed in 50s ====================

✅ 50 секунд вместо 200! Ускорение 4x

Scope уровни

# 1. function (default) — каждый тест @pytest.fixture(scope="function") def temp_user(): user = create_user() yield user delete_user(user) # 2. class — один раз для класса @pytest.fixture(scope="class") def api_client(): client = APIClient() yield client client.close() # 3. module — один раз для файла @pytest.fixture(scope="module") def database(): db = connect_to_db() yield db db.close() # 4. session — один раз для всей сессии @pytest.fixture(scope="session") def docker_container(): container = start_docker() yield container stop_docker(container)

Когда использовать какой scope

ScopeКогда использоватьПримерСоздаётся
functionЧистое состояние для каждого теста (default)User, Order100 раз для 100 тестов
classТесты в одном классеShared state для Test* класса1 раз на класс
moduleМедленный setup для одного файлаDatabase, Cache1 раз на .py файл
sessionОчень медленный setupDocker, Selenium browser1 раз для всех тестов

Практический пример: многоуровневые фикстуры

# conftest.py @pytest.fixture(scope="session") def docker_postgres(): """Docker контейнер PostgreSQL — очень медленно""" print("🐳 Starting PostgreSQL container... (10s)") container = start_postgres_docker() yield container print("🧹 Stopping container...") stop_docker(container) @pytest.fixture(scope="module") def database(docker_postgres): """БД подключение — медленно""" print(f"🔌 Connecting to {docker_postgres.host}... (2s)") db = connect_to_db(docker_postgres.connection_string) yield db print("🧹 Closing connection...") db.close() @pytest.fixture(scope="function") def clean_database(database): """Очищаем БД перед каждым тестом""" print("🧹 Truncating tables...") database.truncate_all() yield database

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

# tests/test_users.py (10 тестов) def test_create_user(clean_database): user = create_user(clean_database, name="Alice") assert user.id is not None # ... ещё 9 тестов

Порядок выполнения:

🐳 Starting PostgreSQL container... (1 раз для всех!)
🔌 Connecting... (1 раз для test_users.py)
🧹 Truncating tables... (10 раз — для каждого теста)
test_create_user PASSED
🧹 Truncating tables...
test_get_user PASSED
...
🧹 Closing connection... (1 раз после всех тестов модуля)
🧹 Stopping container... (1 раз в конце сессии)

✅ Docker запускается 1 раз, БД создаётся 1 раз на файл!

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

Проблема: setup нужен ВСЕГДА

# Хотим чтобы перед каждым тестом: # - Очищался кеш # - Сбрасывался счётчик # - Логировалось имя теста

Решение: autouse=True

@pytest.fixture(autouse=True) def clear_cache(): """Выполняется ПЕРЕД каждым тестом автоматически""" print("🧹 Clearing cache...") cache.clear() def test_user_caching(): # clear_cache уже выполнился! user = get_user(1) assert user is not None def test_product_caching(): # clear_cache снова выполнился! product = get_product(1) assert product is not None

Запуск:

🧹 Clearing cache...
test_user_caching PASSED
🧹 Clearing cache...
test_product_caching PASSED

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

autouse с разными scope

# Перед каждым тестом @pytest.fixture(autouse=True, scope="function") def setup_test(): print("▶️ Test starting...") yield print("✅ Test finished") # Один раз для модуля @pytest.fixture(autouse=True, scope="module") def setup_module(): print("📦 Module setup...") yield print("📦 Module teardown") # Один раз для сессии @pytest.fixture(autouse=True, scope="session") def setup_session(): print("🚀 Session starting...") yield print("🏁 Session finished")

Запуск:

🚀 Session starting...
📦 Module setup...
▶️ Test starting...
test_1 PASSED
✅ Test finished
▶️ Test starting...
test_2 PASSED
✅ Test finished
📦 Module teardown
🏁 Session finished

Практические примеры autouse

1. Логирование тестов

@pytest.fixture(autouse=True) def log_test(request): """Логирует каждый тест""" test_name = request.node.name print(f"\n▶️ Starting: {test_name}") yield print(f"✅ Finished: {test_name}")

2. Установка переменных окружения

@pytest.fixture(autouse=True, scope="session") def setup_env(): """Устанавливает ENV для всех тестов""" os.environ["ENV"] = "test" os.environ["DEBUG"] = "1" yield del os.environ["ENV"] del os.environ["DEBUG"]

3. Очистка состояния

@pytest.fixture(autouse=True) def reset_state(): """Сбрасывает глобальное состояние""" yield # После теста global_cache.clear() global_counter.reset()

Порядок выполнения фикстур

Правила порядка

1. Scope: от широкого к узкому

session → module → class → function

2. Зависимости: от родителя к ребёнку

@pytest.fixture(scope="session") def database(): return connect_to_db() @pytest.fixture(scope="function") def clean_db(database): # Зависит от database database.truncate() return database

Порядок:

  1. database (session) — создаётся первым
  2. clean_db (function) — использует database

Визуализация порядка

@pytest.fixture(scope="session", autouse=True) def a(): print("1. Session setup") yield print("8. Session teardown") @pytest.fixture(scope="module", autouse=True) def b(): print("2. Module setup") yield print("7. Module teardown") @pytest.fixture(autouse=True) def c(): print("3. Function setup") yield print("6. Function teardown") def test_example(): print("4. Test body") assert True print("5. Test finished")

Запуск:

1. Session setup
2. Module setup
3. Function setup
4. Test body
5. Test finished
6. Function teardown
7. Module teardown
8. Session teardown

✅ LIFO (Last In, First Out) для teardown!

Стратегии оптимизации

Стратегия 1: Медленные ресурсы — session scope

# ✅ ХОРОШО — создаётся 1 раз @pytest.fixture(scope="session") def docker_container(): container = start_docker() # 30 секунд yield container stop_docker(container) # ❌ ПЛОХО — создаётся 100 раз @pytest.fixture # scope="function" по умолчанию def docker_container(): container = start_docker() # 30 * 100 = 3000 секунд! yield container stop_docker(container)

Стратегия 2: Изоляция через cleanup, не recreation

# ✅ ХОРОШО — переиспользуем БД @pytest.fixture(scope="module") def database(): db = connect_to_db() # 1 раз yield db db.close() @pytest.fixture(autouse=True) def clean_database(database): # Очищаем, не пересоздаём! database.truncate_all() # ❌ ПЛОХО — пересоздаём БД каждый раз @pytest.fixture def database(): db = connect_to_db() # 100 раз yield db db.close()

Стратегия 3: Ленивая загрузка данных

@pytest.fixture(scope="session") def database(): """Подключение — 1 раз""" return connect_to_db() @pytest.fixture(scope="module") def test_data(database): """Тестовые данные — 1 раз на модуль""" return load_test_data(database) @pytest.fixture def user(test_data): """Конкретный user — на каждый тест""" return test_data["users"][0]

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

  • Scope уровни — session, module, class, function
  • Оптимизация — правильный scope экономит время
  • autouse=True — автоматические фикстуры
  • Порядок выполнения — от широкого scope к узкому
  • Стратегии — переиспользование, изоляция через cleanup
  • Best practices — медленные ресурсы в session scope

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

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

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

Далее: Fixture factories и indirect parametrization