В предыдущей статье я затронул тему pytest fixture, в этой я бы хотел немного раскрыть, что это такое и зачем это использовать.
В обычных тестовых фреймворках (например unittest, JUnit, chai/mocha) для того, чтобы исполнить что-то перед тестом/тестами, используется какая-то функция с названиями типа setup, before. В pytest есть такой же способ, но фикстуры мощнее и функциональнее.
Фикстура - это метод или функция обернутая в декоратор pytest.fixture, которая может вызываться как самостоятельно (всегда), так и по требованию, только при явной передаче в тестовую функцию.
Давайте рассмотрим самый простой пример из документации, вызов фикстуры, только когда она требуется:
import pytest
@pytest.fixture
def alert():
print("Execute fixture before test")
yield
print("Execute fixture after test")
def test1(alert):
print("test1")
def test2():
print("test2")
При исполнении этого кода, мы один раз в консоли увидим:
Execute fixture before test
test1
Execute fixture after test
test2
Это происходит потому, что pytest видит, что тестовая функция явно определяет в параметрах функции фикстуру (по имени функции) и автоматически ее туда передает, предварительно исполнив фикстуру. Если же не будет никакой тестовой функции, куда передана фикстура, то она не будет вызвана ни разу (это можно поменять). Такой вызов фикстуры работает как с функциями, так и с методами класса.
Любая фикстура может быть исполнять код как до тестовой функции, так и после. Код указанный до yield исполняется ДО тестовой функции (setup), после yield - после тестовой функции (teardown). Также, такая фикстура каждый раз исполняется заново, но это поведение можно поменять.
Получение данных из фикстуры
Фикстуры могут отдавать какое-либо значение в тестовые функции и это значение, может быть динамическим. Рассмотрим простой пример:
import os
import pytest
@pytest.fixture
def is_linux():
yield os.name == "posix"
def test1(is_linux):
if is_linux:
do_linux_dps()
else:
do_another()
Фикстура может передавать в тестовую функцию все, что угодно, аналогично обычной python функции.
Параметры фикстуры
Первый параметр это autouse - указывает, что данная фикстура должна быть вызвана все зависимости от того, была она хоть раз передана в тест или нет и будет вызвана перед каждым тестом. Это, например, полезно когда надо подготавливать окружение, тестовые данные или делать что-то, что затрагивает каждый тест, но при этом результат фикстуры функции не нужен.
Следующий параметр - scope, это параметр, который определяет, когда фикстура будет пересоздана (переисполнена?). Он может принимать несколько значений:
- function - значение по-умолчанию, обозначает, что на каждую тестовую функцию, фикстура будет перевыполнена
- class - используется вместе с тестовыми классами и будет исполнена один раз перед созданием тествого класса и после исполнения всех тестов класса.
- module - исполняется один раз при загрузке модуля и по окончанию исполнения всех тестов в этом модуле
- package - тоже самое, но в рамках пакета
- session - фикстура будет исполняться один раз за всю тестовую сессию перед исполнением первого теста и после последнего теста
Давайте посмотрим на примере module, как отработает фикстура
import time
import pytest
@pytest.fixture(scope="module")
def fixture_one():
t = time.time()
print(f"Execute fixture at: {t}")
yield time.time()
print(f"After fixture at: {t}")
def test1(fixture_one):
print(f"Test 1 time {time.time()} fixture: {fixture_one}")
time.sleep(1)
def test2(fixture_one):
print(f"Test 2 time {time.time()} fixture: {fixture_one}")
Запустив это, увидим:
test_one.py::test1 Execute fixture at: 1770932204.870798
Test 1 time 1770932204.870919 fixture: 1770932204.870804
test_one.py::test2 Test 2 time 1770932205.8783429 fixture: 1770932204.870804
After fixture at: 1770932204.870798
Где можем убедиться, что время от фикстуры одинаковое между тестами, но при этом само время поменялось, также, фикстура явно была вызвана только один раз.
Вызов фикстур
В любой тест можно передать любое количество фикстур, но также и фикстуры, могут принимать фикстуры на вход, стоит обращать внимание только на scope фикстур (об этом поговорим в следующей статье).
Очень популярное использование фикстуры в фикстуре, это получение аргументов коммандной строки.
import pytest
@pytest.fixture()
def cmdopt(request):
return request.config.getoption("--our-option")
def test1(cmdopt):
print(cmdopt)
Фикстура cmdopt принимает фикстуру request, которая является встроенной pytest фикстурой, содержащей в себе множество служебной информации о тесте, окружении и т.п.
Думаю, на этом можно закончить ознакомление с фикстурами и более подробно поговорить о них в следующих статьях