Ответ
Использование yield в фикстурах pytest — это идиоматичный способ управления ресурсами, которые требуют явной инициализации (setup) и последующей очистки (teardown).
Этот паттерн разделяет фикстуру на три части:
- Setup: Код, выполняемый до
yield. Здесь происходит создание и настройка ресурса (например, подключение к БД, запуск веб-драйвера, создание временного файла). - Yield: Ключевое слово, которое передает созданный ресурс в тест и приостанавливает выполнение фикстуры.
- Teardown: Код, выполняемый после
yield. Он гарантированно выполняется после завершения теста (даже если тест упал), обеспечивая корректное освобождение ресурса (закрытие соединения, удаление файла).
Пример с подключением к базе данных:
import pytest
class DatabaseConnection:
def __init__(self, dsn):
print(f"n[SETUP] Connecting to {dsn}...")
self._connection = "<connection_object>"
def query(self, sql):
return f"Executing '{sql}'"
def close(self):
print("n[TEARDOWN] Closing connection...")
@pytest.fixture(scope="module")
def db_connection():
# 1. Setup-фаза
conn = DatabaseConnection("postgresql://user:pass@host/db")
# 2. Передача ресурса в тест
yield conn
# 3. Teardown-фаза (гарантированно выполнится)
conn.close()
def test_user_query(db_connection):
result = db_connection.query("SELECT * FROM users")
assert "users" in result
Этот подход является более чистой и надежной альтернативой addfinalizer, так как логика setup и teardown находится в одном месте и работает аналогично конструкции try...finally.
Ответ 18+ 🔞
Давай разберём эту тему про yield в фикстурах, а то некоторые до сих пор, блядь, как обезьяны с гранатой — код пишут, а потом ресурсы не закрывают, и сервера падают, ёпта!
Вот смотри, есть у тебя какая-нибудь штука, которая требует уборки после себя. Например, подключение к базе данных, которое ты открыл, поработал и должен закрыть. Если не закроешь — получишь утечку, и потом админ тебе, пидарас шерстяной, всю ночь будет звонить, спрашивая, кто это у него все соединения в PostgreSQL съел.
Раньше, в каменном веке, для этого использовали addfinalizer. Но это было неудобно, блядь, как ехать на велосипеде с квадратными колёсами. Логика setup и teardown была размазана по коду, и можно было легко запутаться.
А теперь есть красота — yield. Это как разбить фикстуру на три понятных куска, прямо как слоёный пирог, только без мака.
- Сначала готовим (Setup). Всё, что до
yield. Тут ты ресурс создаёшь: базу поднимаешь, файл открываешь, браузер запускаешь. - Потом отдаём и ждём (Yield). Сам
yield— это волшебная пауза. Он отдаёт твой готовый ресурс тесту и замирает, пока тест работает. Представь, что ты передал другу свою игровую приставку и стоишь рядом, ждёшь, пока он поиграет. - Наконец, убираем за собой (Teardown). Всё, что после
yield. Этот код выполнится гарантированно, даже если тест упадёт с криком "AssertionError!" или его убьёт таймаут. Это твой шанс всё прибрать: соединение закрыть, файл удалить, браузер прибить. Как конструкцияtry...finally, только элегантнее, сука.
Вот смотри на живой пример. Представь, что у нас есть класс для подключения к базе, который мы сами написали, пока начальник не видел.
import pytest
class DatabaseConnection:
def __init__(self, dsn):
print(f"n[SETUP] Connecting to {dsn}...")
self._connection = "<connection_object>"
def query(self, sql):
return f"Executing '{sql}'"
def close(self):
print("n[TEARDOWN] Closing connection...")
@pytest.fixture(scope="module")
def db_connection():
# 1. Всё, что до yield — настраиваем
conn = DatabaseConnection("postgresql://user:pass@host/db")
# 2. Отдаём подключение тесту и замираем
yield conn
# 3. Всё, что после yield — прибираем (сработает ВСЕГДА)
conn.close()
def test_user_query(db_connection):
result = db_connection.query("SELECT * FROM users")
assert "users" in result
Что здесь происходит? Фикстура db_connection создаёт объект подключения, говорит "на, тест, работай" (yield conn), а потом, когда тест отвалится (в хорошем смысле), она выполнит conn.close(). Даже если твой тест словит исключение посреди запроса — соединение всё равно закроется. Красота, да и только!
Это, блядь, и есть идиоматичный способ в pytest. Чисто, надёжно, и не надо изобретать велосипед с реактивным двигателем. Просто запомни: yield — это не только для генераторов, но и для того, чтобы не быть свиньёй и не засирать память открытыми ресурсами.