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

Ответ

В pytest для управления жизненным циклом ресурсов (например, закрытие соединений с БД, удаление временных файлов) используются фикстуры. Очистка (teardown) реализуется двумя основными способами.

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

Это наиболее распространенный и читаемый подход. Код до yield является setup-частью, а код после yieldteardown-частью, которая гарантированно выполнится после завершения теста.

Пример с временным файлом:

import pytest
import os

@pytest.fixture
def temp_file():
    # Setup: создаем файл
    file_path = "test_temp_file.txt"
    with open(file_path, "w") as f:
        f.write("hello")

    yield file_path  # Передаем ресурс в тест

    # Teardown: удаляем файл
    print(f"nCleaning up {file_path}...")
    os.remove(file_path)

def test_read_temp_file(temp_file):
    with open(temp_file, "r") as f:
        assert f.read() == "hello"

2. Использование request.addfinalizer

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

Пример:

import pytest

class DatabaseConnection:
    def __init__(self, name):
        self.name = name
        self.is_open = True
        print(f"nDB '{self.name}' connection opened.")

    def close(self):
        self.is_open = False
        print(f"nDB '{self.name}' connection closed.")

@pytest.fixture
def db_conn(request):
    conn = DatabaseConnection("main_db")

    # Регистрируем функцию, которая будет вызвана для очистки
    request.addfinalizer(conn.close)

    return conn

def test_db_operations(db_conn):
    assert db_conn.is_open

Ключевое различие: yield — это синтаксический сахар, который более нагляден для простого сценария "setup/teardown". addfinalizer предоставляет больше гибкости, но немного усложняет код.

Ответ 18+ 🔞

Да ты посмотри, какие у нас тут инструменты для уборки после тестов в pytest! Фикстуры, блядь, — это наше всё. Чтобы не оставлять за собой говно в виде открытых соединений или временных файлов, есть два основных подхода. Выбирай, какой тебе больше нравится, но первый — это вообще огонь.

1. Через yield (самый прикольный и читаемый)

Здесь всё просто, как три копейки. Всё, что до yield — это подготовка (setup), а всё, что после — это та самая зачистка (teardown), которая выполнится в любом случае, даже если тест обосрётся с ошибкой.

Вот тебе пример с файлом, который надо удалить после себя:

import pytest
import os

@pytest.fixture
def temp_file():
    # Setup: создаем файл
    file_path = "test_temp_file.txt"
    with open(file_path, "w") as f:
        f.write("hello")

    yield file_path  # Передаем файл тесту и ждём, пока он там нахулиганит

    # Teardown: прибираемся, как мама за маленьким ребёнком
    print(f"nCleaning up {file_path}...")
    os.remove(file_path)

def test_read_temp_file(temp_file):
    with open(temp_file, "r") as f:
        assert f.read() == "hello"

2. Через request.addfinalizer (для гиков и сложных случаев)

Этот способ — как швейцарский нож: мощнее, но и покруче будет. Он позволяет навешивать несколько функций для очистки, если тебе вдруг захотелось поизвращаться.

Смотри, как можно сымитировать подключение к базе:

import pytest

class DatabaseConnection:
    def __init__(self, name):
        self.name = name
        self.is_open = True
        print(f"nDB '{self.name}' connection opened.")

    def close(self):
        self.is_open = False
        print(f"nDB '{self.name}' connection closed.")

@pytest.fixture
def db_conn(request):
    conn = DatabaseConnection("main_db")

    # Говорим питону: "Слушай, дружок, когда всё закончится, не забудь вызвать эту функцию, а то будет атас"
    request.addfinalizer(conn.close)

    return conn

def test_db_operations(db_conn):
    assert db_conn.is_open

Так в чём же разница, спросишь ты? yield — это красиво и прямо в лоб, идеально для 90% случаев. А addfinalizer — это когда тебе мало жизни и хочется поуправлять процессом точечнее, например, если нужно зарегистрировать овердохуища разных финализаторов. Но честно? Если задача простая — бери yield и не парься.