Pytest fixtures (фікстури) – це функції, які використовуються для підготовки середовища перед виконанням тестів, та його очищення по завершенню. Це може включати ініціалізацію об’єктів, налаштування з’єднання з базою даних, завантаження та генерацію тестових даних, тощо.
Фікстури також можуть бути використані в якості параметризованих аргументів для тестових функцій, щоб забезпечити різні вхідні дані для тесту.
Приклади використання pytest fixture
Простий приклад фікстури, що створює та ініціює об’єкт перед тестом:
import pytest
from faker import Faker
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def get_full_name(self):
return f'{self.first_name} {self.last_name}'
fake = Faker(locale='uk_UA')
first_name, last_name = fake.first_name(), fake.last_name()
@pytest.fixture
def person():
person = Person(first_name, last_name)
return person
def test_person_data(person):
assert person.get_full_name() == f'{first_name} {last_name}'- для створення фікстури використовується декоратор @pytest.fixture (рядок 19)
- щоб використати фікстуру (об’єкт котрий вона повертає) в тілі тестової функції, потрібно передати її в якості параметру тестовій функції (рядок 25)
В наступному прикладі фікстура з ім’ям browser створює об’єкт webdriver.Chrome() перед тестом:
import pytest
from selenium import webdriver
@pytest.fixture
def browser():
driver = webdriver.Chrome()
yield driver
driver.quit()
def test_title(browser):
browser.get('https://docs.pytest.org/')
expected_title = 'pytest: helps you write better programs — pytest documentation'
assert browser.title == expected_titleВ цьому прикладі фікстура browser не тільки повертає об’єкт драйвера, котрий використовується безпосередньо в тестовій функції для взаємодії зі сторінкою. Також вона налаштовує дії, що будуть автоматично виконані після завершення тесту, а саме закриття вікна браузера (рядок 9).
pytest підтримує виконання фікстурами специфічного завершального коду, під час виходу з області дії. Якщо використати оператор yield замість return, весь код після yield виконує роль «прибиральника».
Приклад використання параметризованої фікстури
В прикладі нижче, фікстура browser параметризована двома параметрами (chrome, edge), для кожного з котрих буде створено окрему фікстуру, що буде ініціювати та повертати відповідний об’єкт драйвера. Тестова функція test_title буде виконана з кожною фікстурою, тобто в двох екземплярах браузера (chrome, edge):
import pytest
from selenium import webdriver
@pytest.fixture(params=['chrome', 'edge'])
def browser(request):
browser = request.param
driver = None
if browser == 'chrome':
driver = webdriver.Chrome()
elif browser == 'edge':
driver = webdriver.Edge()
yield driver
driver.quit()
def test_title(browser):
browser.get('https://docs.pytest.org/')
expected_title = 'pytest: helps you write better programs — pytest documentation'
assert browser.title == expected_titleВарто звернути увагу, що фікстури також можуть використовувати інші фікстури. В цьому прикладі фікстура browser використовує вбудовану фікстуру request (саме виклик request.param дає доступ до параметрів).
Докладніше про параметризацію тестів в pytest, можна ознайомитися в нотатці: Pytest Python – параметризація тестів.
Області дії фікстур (scope)
У pytest є кілька областей дії для фікстур (scope), які визначають коли саме фікстура буде створена та знищена, під час виконання тестів. Перелік scope для фікстур:
- function – фікстура буде створена і знищена один раз, перед і після кожної тестової функції (це значення є дефолтним, якщо scope не задано)
- class – фікстура буде створена і знищена один раз, перед і після виконання всіх тестів в класі
- module – фікстура буде створена і знищена один раз, перед і після виконання всіх тестів в модулі
- package – фікстура буде створена і знищена один раз, перед і після виконання всіх модулів що містять тести, всередині пакету (цей scope було додано в версії pytest 3.7)
- session – фікстура буде створена один раз перед виконанням всіх тестів у сесії і знищена після завершення сесії (після виконання всіх тестів)
Область дії визначається не обов’язковим параметром scope у декораторі @pytest.fixture. Наприклад:
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
@pytest.fixture(scope='module')
def browser():
driver = webdriver.Chrome()
driver.get('https://docs.pytest.org/')
yield driver
driver.quit()
def test_title(browser):
expected_title = 'pytest: helps you write better programs — pytest documentation'
assert browser.title == expected_title
def test_h1(browser):
expected_h1 = 'pytest: helps you write better programs'
assert browser.find_element(by=By.TAG_NAME, value='h1').text == expected_h1В цьому прикладі browser має scope=module. Перед виконанням першого тесту в модулі, буде створено об’єкт драйвера та відкрито веб-сторінку для тестування. По завершенню всіх тестів в модулі, буде закрито браузер та знищено об’єкт драйвера.
Використання @pytest.mark.usefixtures
@pytest.mark.usefixtures – додавання цього декоратора до тестової функції, дозволяє автоматично зробити виклик фікстур, перед початком тесту. Перелік необхідних фікстур встановлюється в параметрі pytest.mark.usefixtures. Приклад:
import pytest
@pytest.fixture
def setup():
print("test setup")
@pytest.mark.usefixtures("setup")
def test_example():
print("test execution")test_example не приймає фікстуру setup як аргумент і не потребує доступу до неї в тілі тестової функції, але перед початком тесту потрібен виклик setup для певних налаштувань. Декоратор @pytest.mark.usefixtures(“setup”), доданий до тестової функції, повідомляє pytest про це.
Також декоратор @pytest.mark.usefixtures можна використовувати на рівні класу.
Використання autouse=True
Параметр autouse=True (за замовчуванням він встановлений як False) в фікстурі pytest, означає що вона буде виконана автоматично перед кожним тестом, в межах scope котрий їй задано.
Зазвичай фікстури викликаються тільки тоді, коли їх передають як параметри до тестової функції, але autouse=True дозволяє автоматично викликати фікстуру, без додавання в параметри тесту. Приклад:
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
@pytest.fixture(scope='class', autouse=True)
def browser(request):
driver = webdriver.Chrome()
request.cls.driver = driver
driver.get('https://docs.pytest.org/')
yield
driver.quit()
class TestPage:
def test_title(self):
expected_title = 'pytest: helps you write better programs — pytest documentation'
assert self.driver.title == expected_title
def test_h1(self):
expected_h1 = 'pytest: helps you write better programs'
assert self.driver.find_element(by=By.TAG_NAME, value='h1').text == expected_h1В цьому прикладі фікстура browser буде автоматично викликана перед початком виконання тестів в класі TestPage. Вона створює об’єкт драйвера та встановлює атрибут driver для тестового класу, завдяки можливостям вбудованої фікстури request (рядок 9).
Файл conftest.py
Фікстури визначені в модулі conftest.py, можуть використовуватися будь-яким тестом в пакеті де знаходиться conftest.py, без необхідності їх імпортувати (pytest автоматично виявить їх перед виконанням тестів).
Для простого прикладу уявімо що в мене є наступна структура проекту:

Додам до модуля conftest.py фікстуру browser:
import pytest
from selenium import webdriver
@pytest.fixture(scope='session')
def browser():
driver = webdriver.Chrome()
yield driver
driver.quit()Для того щоб використати фікстуру browser в модулі test_google.py, котрий знаходиться в тому самому пакеті що і conftest.py, достатньо просто вказати ім’я фікстури в параметрі тестової функції:
def test_title(browser):
browser.get('https://www.google.com/')
assert browser.title == 'Google'
Таким чином, модуль conftest.py можна використовувати для зберігання загальних фікстур, використання котрих не обмежено одним модулем.