← Назад к вопросам

Как сделать, чтобы фикстура была вызвана после завершения?

1.7 Middle🔥 221 комментариев
#Теория тестирования#Фреймворки тестирования

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Управление вызовом фикстур после завершения теста

В Pytest для управления временем вызова фикстур используются scope и механизм finalizer/yield. Чтобы фикстура выполняла какие-либо действия ПОСЛЕ завершения теста или сессии, существует несколько основных подходов.

1. Фикстура с использованием yield (контекстный менеджер)

Это наиболее распространённый и рекомендуемый способ. Код после yield выполняется после завершения теста (или в зависимости от scope).

import pytest

@pytest.fixture(scope="function")
def resource_setup_teardown():
    # Действия ДО теста
    print("\nНастройка ресурсов перед тестом")
    resource = {"data": "test_data", "active": True}
    
    yield resource  # Передаём ресурс в тест
    
    # Действия ПОСЛЕ теста
    print("Очистка ресурсов после теста")
    resource["active"] = False
    del resource["data"]

Важные особенности:

  • Код после yield выполняется независимо от результата теста (pass/fail)
  • Можно использовать различные scope: function, class, module, session
  • Если возникает исключение до yield, код после yield не выполняется

2. Использование addfinalizer для более сложных сценариев

Этот подход полезен, когда нужно зарегистрировать несколько финализаторов или когда логика финализатора определяется динамически.

import pytest

@pytest.fixture(scope="module")
def database_connection(request):
    # Инициализация соединения
    conn = {"status": "connected", "session_id": 12345}
    print(f"Создано соединение: {conn['session_id']}")
    
    def close_connection():
        # Этот код выполнится после завершения
        print(f"Закрытие соединения: {conn['session_id']}")
        conn["status"] = "disconnected"
        # Здесь может быть реальное закрытие БД, файла и т.д.
    
    # Регистрируем финализатор
    request.addfinalizer(close_connection)
    
    return conn

Преимущества addfinalizer:

  • Можно добавить несколько финализаторов
  • Финализаторы выполняются в обратном порядке добавления
  • Работает даже если исключение происходит до возврата фикстуры

3. Фикстура с scope="session" для глобальной очистки

Для действий, которые должны выполниться один раз после ВСЕХ тестов:

import pytest
import tempfile
import os

@pytest.fixture(scope="session", autouse=True)
def global_cleanup(request):
    # Создаём временную директорию для всей сессии
    temp_dir = tempfile.mkdtemp()
    print(f"Создана временная директория: {temp_dir}")
    
    def remove_temp_dir():
        # Выполнится после ВСЕХ тестов
        print(f"Удаление временной директории: {temp_dir}")
        import shutil
        if os.path.exists(temp_dir):
            shutil.rmtree(temp_dir)
    
    request.addfinalizer(remove_temp_dir)
    return temp_dir

4. Автоматическое использование фикстур (autouse=True)

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

import pytest

@pytest.fixture(scope="function", autouse=True)
def log_test_duration(request):
    import time
    start_time = time.time()
    
    yield
    
    duration = time.time() - start_time
    print(f"Тест {request.node.name} выполнен за {duration:.2f} секунд")
    # Здесь можно логировать в файл, отправлять в мониторинг и т.д.

5. Комбинированный пример с обработкой исключений

import pytest

@pytest.fixture(scope="function")
def file_processor(request):
    files_to_cleanup = []
    
    def create_test_file(filename):
        with open(filename, 'w') as f:
            f.write("test content")
        files_to_cleanup.append(filename)
        return filename
    
    def cleanup_files():
        import os
        print(f"Очистка {len(files_to_cleanup)} файлов")
        for file in files_to_cleanup:
            try:
                if os.path.exists(file):
                    os.remove(file)
                    print(f"Удалён файл: {file}")
            except Exception as e:
                print(f"Ошибка при удалении {file}: {e}")
    
    request.addfinalizer(cleanup_files)
    
    return create_test_file

# Использование в тесте
def test_file_operations(file_processor):
    file1 = file_processor("test1.txt")
    file2 = file_processor("test2.txt")
    # Оба файла будут удалены ПОСЛЕ теста

Ключевые моменты для выбора подхода:

  1. yield — идеален для простых сценариев setup/teardown
  2. addfinalizer — выбирайте когда нужно:
    • Несколько независимых финализаторов
    • Динамическое создание ресурсов
    • Гарантированное выполнение даже при ошибках в setup
  3. scope определяет когда выполнится финализатор:
    • function — после каждого теста
    • class — после всех тестов класса
    • module — после всех тестов модуля
    • session — после всей сессии тестов
  4. autouse=True — когда фикстура должна применяться ко всем тестам автоматически

Правильное использование этих механизмов позволяет создавать надёжные тесты с гарантированным освобождением ресурсов, что особенно критично при работе с базами данных, сетевыми соединениями, файловой системой или внешними API.