Ответ
В 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 вполне достаточно.