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 можна використовувати для зберігання загальних фікстур, використання котрих не обмежено одним модулем.


Мітки: