В чем разница между yield и return в фикстурах PyTest?

Ответ

В PyTest yield и return используются в фикстурах для передачи данных в тест, но с фундаментальным отличием в возможности выполнить код очистки (teardown) после завершения теста.

Фикстура с return

  • Передаёт значение в тест и завершает работу.
  • Не предоставляет встроенного механизма для очистки. Если ресурс нужно закрыть, это делается отдельно (например, в финальной фазе теста или с помощью request.addfinalizer).

Фикстура с yield

  • Работает как генератор. Код до yield выполняется как установка (setup), а код после yield — как очистка (teardown).
  • Очистка выполняется гарантированно, даже если тест упал с ошибкой (assertion или exception).
  • Это идеальный способ для работы с ресурсами, требующими закрытия (файлы, соединения с БД, браузеры).

Практический пример с временным файлом:

import pytest
import os

# Фикстура с yield (рекомендуемый способ для ресурсов)
@pytest.fixture
def temp_config_file():
    # SETUP: Создаём временный файл перед тестом
    file_path = "/tmp/test_config.yaml"
    with open(file_path, 'w') as f:
        f.write("test_key: test_value")
    print("Файл создан")

    yield file_path  # Передаём путь к файлу в тест

    # TEARDOWN: Удаляем файл после теста (выполнится всегда)
    os.remove(file_path)
    print("Файл удалён")

# Фикстура с return и addfinalizer (альтернативный способ)
@pytest.fixture
def temp_config_file_v2(request):
    file_path = "/tmp/test_config_v2.yaml"
    with open(file_path, 'w') as f:
        f.write("test_key: test_value")
    print("Файл v2 создан")

    def cleanup():
        os.remove(file_path)
        print("Файл v2 удалён")

    request.addfinalizer(cleanup)  # Регистрируем функцию очистки
    return file_path  # Передаём путь

# Тест использует фикстуру

def test_read_config(temp_config_file):
    # В тест передан file_path
    with open(temp_config_file) as f:
        content = f.read()
    assert "test_key" in content
    # После этого теста PyTest автоматически выполнит os.remove(file_path)

Вывод: Для управления жизненным циклом ресурсов (открыть/закрыть) всегда используйте фикстуры с yield. return подходит для простых данных, не требующих очистки.

Ответ 18+ 🔞

А, ну вот, опять про эти фикстуры в PyTest спрашивают! Ебать мои старые костыли, каждый раз одно и то же объясняю. Слушай сюда, сейчас разложу по полочкам, чтобы даже мартышлюшка поняла.

В общем, есть две штуки — yield и return. И разница между ними, чувак, как между "взял попить" и "взял попить, а потом поставил стакан обратно в шкаф, а не оставил на столе, как последний распиздяй".

Фикстура с return — это просто, блядь, передал значение в тест и пошёл на хуй. Всё. Точка. Очистки никакой нет. Если ты файл открыл или базу подключил — сам потом закрывай, терпения ебать ноль, чтобы за тобой убирали.

Фикстура с yield — это уже хитрая жопа. Она работает как генератор. Всё, что до yield — это настройка (setup), типа "открыл файл, подключился к базе". Потом yield передаёт управление тесту вместе с данными. А ВСЁ, ЧТО ПОСЛЕ yield — это гарантированная уборка (teardown). Даже если тест упал с пиздец-ошибкой, даже если ты сам от себя охуел от результата — очистка выполнится. Это как договор с дьяволом, только полезный.

Простой пример, чтобы не ебать копать: Допустим, тебе нужен временный файл для теста.

С return — это пиздопроебибна. Создал файл, вернул путь, а удалять его — это уже твои проблемы. Забудешь — диск засрется.

А вот с yield — красота, ёпта. Создал файл, yieldнул путь тесту, тест отработал (или не отработал), и ПОТОМ, автоматически, файл удаляется. Волнение ебать — ноль. Всё чисто.

Смотри, как это выглядит в коде. Блоки кода не трогаю, они святые:

import pytest
import os

# Вот фикстура с yield — правильный путь, чувак
@pytest.fixture
def temp_config_file():
    # SETUP: Делаем файл перед тестом
    file_path = "/tmp/test_config.yaml"
    with open(file_path, 'w') as f:
        f.write("test_key: test_value")
    print("Файл создан")

    yield file_path  # Отдаём путь тесту и как бы замираем

    # TEARDOWN: А вот это выполнится ПОСЛЕ теста в любом случае!
    os.remove(file_path)
    print("Файл удалён")

# А это фикстура с return и костылём в виде addfinalizer
@pytest.fixture
def temp_config_file_v2(request):
    file_path = "/tmp/test_config_v2.yaml"
    with open(file_path, 'w') as f:
        f.write("test_key: test_value")
    print("Файл v2 создан")

    def cleanup(): # Функция-уборщица
        os.remove(file_path)
        print("Файл v2 удалён")

    request.addfinalizer(cleanup)  # Регистрируем уборщицу
    return file_path  # И возвращаем путь

# Сам тест
def test_read_config(temp_config_file):
    # Тут у нас уже лежит file_path
    with open(temp_config_file) as f:
        content = f.read()
    assert "test_key" in content
    # И после assert PyTest сам вызовет os.remove(file_path) из фикстуры!

Итог, блядь: Если тебе нужно просто передать в тест цифру, строку или список — return и да похуй. Но если работаешь с чем-то, что нужно ОБЯЗАТЕЛЬНО закрыть, освободить, удалить, отключить — это твой верный друг yield. Не усложняй себе жизнь, используй правильный инструмент.