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

«Как сделать, чтобы код в фикстуре выполнился после завершения теста» — вопрос из категории Фреймворки тестирования, который задают на 24% собеседований AQA / Automation. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В pytest есть два основных способа выполнить код после теста (teardown) в фикстуре: с помощью оператора yield или используя request.addfinalizer. Я предпочитаю yield за его чистоту и читаемость.

1. Способ с yield (рекомендуемый и самый частый в моей практике):

Код до yield выполняется как setup, код после — как teardown. yield может возвращать значение в тест.

import pytest

@pytest.fixture
def test_database_connection():
    """Фикстура устанавливает соединение с БД и гарантированно закрывает его после теста."""
    print("Устанавливаю соединение с тестовой БД...")
    connection = connect_to_db("test_db")  # Setup-действие

    yield connection  # Передаём объект соединения в тест
    # Всё после yield — teardown

    print("Закрываю соединение с БД...")
    connection.close()  # Teardown-действие
    cleanup_test_data(connection)  # Дополнительная очистка

def test_fetch_users(test_database_connection):
    """Тест получает connection из фикстуры."""
    users = test_database_connection.query("SELECT * FROM users")
    assert len(users) > 0
    # После завершения этого теста автоматически выполнится код после yield в фикстуре

2. Способ с addfinalizer (полезен, когда нельзя использовать yield):

Этот способ гибче, позволяет регистрировать несколько функций окончания.

import pytest

@pytest.fixture
def temporary_test_file(request):
    """Создаёт временный файл и удаляет его после теста."""
    file_path = "/tmp/test_data.json"
    with open(file_path, 'w') as f:
        f.write('{"test": "data"}')
    print(f"Создан файл: {file_path}")

    # Регистрируем функцию teardown
    def delete_file():
        print(f"Удаляю файл: {file_path}")
        import os
        os.remove(file_path)

    request.addfinalizer(delete_file)
    return file_path

Ключевое отличие: Код teardown при использовании yield выполнится только после успешного выполнения setup. addfinalizer зарегистрирует teardown-функцию, которая вызовется даже если setup-код фикстуры упадёт с ошибкой до yield или return. В большинстве случаев yield вполне достаточно.