Не понимаю, как использовать yield в автотестах

Рейтинг: 3Ответов: 2Опубликовано: 19.03.2023

Изучаю автотестирование и возникла проблема. В одной статье написано:

yield — это разделитель, все, что написано над ним, будет исполнено до теста, все, что ниже — после теста.

И приведен пример написания фикстуры:

@pytest.fixture(autouse=True)
def get_driver(request):
    service = Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=service)
    request.cls.driver = driver
    yield
    driver.quit()

Тут yield без каких-либо переменных прописан.

В другом месте я увидела другой пример:

@pytest.fixture(scope="session")
def browser():
    driver = webdriver.Chrome(executable_path="./chromedriver")
    yield driver
    driver.quit()

Здесь уже yield driver.

Я не понимаю, когда нужно указывать переменную, а когда нет.

Ответы

▲ 3Принят

@Nati, спасибо что добавили ссылки, а вот звездочки в коде вопроса зря, я уже думал что-то упустил из Python смотря на запись **yield driver** поэтому их (звездочки) лучше убрать. yield driver — это все равно что return driver (очень грубо но суть такая же) т.е. иногда в функциях Вы пишите просто return а иногда return value тут тоже самое можно yield а можно yield driver

Приведу пример на основе документации (только упрощу и return заменю на yield):

файл test_sample.py

import pytest

@pytest.fixture
def my_fruit():
    yield "apple"


@pytest.fixture
def fruit_basket(my_fruit):
    return ["banana", "apple"]


def test_my_fruit_in_basket(my_fruit, fruit_basket):
    assert my_fruit in fruit_basket

Тут мы проверяем наличия банана в нашей корзине.

$ pytest
========================= test session starts =========================
platform linux -- Python 3.10.6, pytest-7.2.2, pluggy-1.0.0
rootdir: ...
collected 1 item                                                      

test_sample.py .                                                [100%]

========================== 1 passed in 0.01s ==========================

А теперь я верну просто yield без банана т.е. так:

@pytest.fixture
def my_fruit():
    yield

Результат:

$ pytest
========================= test session starts =========================
platform linux -- Python 3.10.6, pytest-7.2.2, pluggy-1.0.0
rootdir: ...
collected 1 item                                                      

test_sample.py F                                                [100%]

============================== FAILURES ===============================
_______________________ test_my_fruit_in_basket _______________________

my_fruit = None, fruit_basket = ['banana', 'apple']

    def test_my_fruit_in_basket(my_fruit, fruit_basket):
>       assert my_fruit in fruit_basket
E       AssertionError: assert None in ['banana', 'apple']

test_sample.py:24: AssertionError
======================= short test summary info =======================
FAILED test_sample.py::test_my_fruit_in_basket - AssertionError: assert None in ['banana', 'apple']
========================== 1 failed in 0.05s ==========================

В данном случае мы видим упавшие тесты. Т.е. В общем случае вопрос не сильно связан с тестами, а больше с возвращаемым значением. Если нам где-то нужен driver мы его возвращаем, не нужен делаем просто yield отличие которого от return только лишь в том что при повторном запуске сработает не этот а следующий за ним yield если он есть, например так:

def my_fruit():
    yield 'apple'  # <--- вернет в первый раз
    yield 'kivi'   # <--- вернет во второй раз

Но конкретно в фикстурах yield должен быть один!

____________ ERROR at teardown of test_my_fruit_in_basket _____________
fixture function has more than one 'yield':

@pytest.fixture
def my_fruit():
    yield "apple"
    yield "kivi"

Это связано с освобождением ресурсов после выполнения кода, например: закрытие driver

Одним словом, не знаете возвращать или нет — возвращайте, много не потеряете.

Надеюсь, моих объяснений окажется достаточно, чтобы двигаться дальше.

▲ 0

Данная запись фикстуры:

@pytest.fixture(autouse=True)
def get_driver(request):
    service = Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=service)
    request.cls.driver = driver
    yield
    driver.quit()

подразумевает такое использование в тесте:

def test_1(request):
   some_driver = request.driver
   ...

Что происходит в данном случае? В таком сценарии есть другая фикстура request, чтобы обратиться к этой фикстуре - она указывается в аргументах тест-кейса, а фикстура get_driver вызывается автоматически, благодаря использованию параметра autouse=True, в фикстуре get_driver мы тоже используем фикстуру request (фикстуры могут использовать другие фикстуры), и в данном случаем мы модифицируем объект request, предоставляемый той фикстурой, благодаря чему driver становится доступным в тесте.

Другая запись фикстуры

@pytest.fixture(scope="session")
def browser():
    driver = webdriver.Chrome(executable_path="./chromedriver")
    yield driver
    driver.quit()

Предполагает другой сценарий использования в тесте:

def test_1(browser):
   some_driver = browser
   ...

В данном случае мы указываем фикстуру в параметрах тест-кейса, в качестве значения этого параметра мы получим непосредственно то, что фикстура вернула - через операторы return или yield - то есть объект driver. После завершения тест-кейса фреймворк pytest снова пройдет по фикстурам и выполнит код который указан после оператора yield (в случае если таковой имеется).