Skip to content

13. Pytest

qahelping edited this page Dec 7, 2025 · 3 revisions

Данный раздел разделен на несколько частей(каждый пункт - это ссылка на соответствующий раздел):

Переход по внутренним ссылкам происходит только при открытом разделе блока в котором находится ссылка.



Pytest - это фреймворк для написания тестов на языке Python. Он позволяет писать простые и сложные тесты, а также управлять ими. Pytest позволяет писать тесты в стиле BDD, а также позволяет использовать маркировку тестов, фикстуры и параметризацию.

Установка Pytest

Для большинства пакетов Python, установка доступна с помощью команды pip. Для установки pytest, выполните следующую команду:

python -m pip install pytest

Аргументы запуска

Увидеть все команды для pytest можно с помощью команды pytest --help

pytest --co (pytest --collect-only) # позволяет вывести все доступные тесты в директории/проекте
pytest -k "вхождение строки любого теста" # запуск конкретного теста
pytest -m "марка теста" # запуск тестов с определённой маркой
pytest --markers # вывод всех доступных марок в проекте
pytest --fixtures # выводит список всех доступных фикстур
pytest --durations=x # вывод x самых долгих тестов
pytest -l (pytest --showlocals) # выводит локальные переменные в тестах
pytest --setup-plan # тесты не запускает, но выводит план запуска тестов
pytest -v (pytest --verbose) # выводит более подробную информацию о тестах
pytest -s # выводит вывод тестов в реальном времени
pytest -rfEX # выводит только ошибки и отчёт о тестах

Примеры использования аргументов:

--co

Нажать, чтобы раскрыть
pytest --co

Если нужно вывести все тесты в определённом файле, то нужно указать путь к файлу(или название файла) после --co:

pytest --co tests/test_example.py

-k

Нажать, чтобы раскрыть
pytest --co -k mobile

Где слово mobile - это часть названия теста, которое мы хотим увидеть в проекте. В данном случае, мы увидим все тесты, в названии которых есть слово mobile.

Если нужно запустить все тесты, где есть слово mobile, то нужно использовать команду:

pytest -k mobile

Если необходимо запустить несколько тестов, с разными словами в названии, то можно использовать логические операторы and, or, not:

pytest -k "mobile or desktop"

Командной выше будут запущены все тесты, где есть слово mobile или desktop.

Если необходимо указать более сложное условие, то можно использовать скобки:

pytest -k "(homework and mobile) or test_first"

Командной выше будут запущены все тесты, где есть слово homework и mobile, а также тест с названием test_first.

-m

Нажать, чтобы раскрыть
pytest -m slow

Команда равноценна

pytest -m "slow"

Если необходимо запустить несколько марок, то можно использовать логические операторы and, or, not:

pytest -m "slow or fast"

Командной выше будут запущены все тесты, где есть марка slow или fast.

--markers

Нажать, чтобы раскрыть
pytest --markers

--fixtures

Нажать, чтобы раскрыть
pytest --fixtures

При запуске команды pytest --fixtures выводятся все доступные фикстуры в проекте.

Если фикстура имеет параметры, то они также отображаются.

cache - это фикстура, которая используется для кеширования результатов тестов. Если тесты выполняются медленно, то можно использовать эту фикстуру для ускорения выполнения тестов.

capsysbinary - это фикстура, которая используется для перехвата вывода тестов, используя использует бинарный вывод.

capfd - это фикстура, которая используется для перехвата вывода тестов.

capfdbinary - это фикстура, которая используется для перехвата вывода тестов на более низком уровне, используя бинарный вывод.

capsys - это фикстура, которая используется для перехвата вывода тестов.

doctest_namespace - это фикстура, которая используется для передачи пространства имен в doctest.

pytestconfig - это фикстура, которая используется для доступа к конфигурации pytest.

record_property - это фикстура, которая используется для записи свойств тестов.

record_xml_attribute - это фикстура, которая используется для записи атрибутов XML.

record_testsuite_property - это фикстура, которая используется для записи свойств тестов.

tmpdir_factory - это сессионная фикстура, которая может использоваться для создания произвольных временных каталогов из любой другой фикстуры или теста.

tmpdir - это фикстура, которая используется для создания временных каталогов.

caplog - это фикстура, которая используется для перехвата вывода тестов.

monkeypatch - это фикстура, которая используется для изменения поведения тестов.

recwarn - это фикстура, которая используется для записи предупреждений тестов.

tmp_path_factory - это сессионная фикстура, которая предоставляет временный каталог, уникальный для вызова теста, созданный в базовом временном каталоге.

tmp_path - это фикстура, которая предоставляет временный каталог, уникальный для вызова теста, созданный в базовом временном каталоге.

--setup-plan

Нажать, чтобы раскрыть
pytest --setup-plan

Выполнение команды pytest --setup-plan позволяет увидеть план запуска тестов в проекте.

Если нужно увидеть план запуска тестов определенного файла, то нужно указать название файла и ключ --setup-plan:

pytest test_simple.py --setup-plan  

--durations

Нажать, чтобы раскрыть
pytest test_simple.py --durations=2

Выполнение команды pytest test_simple.py --durations=2 позволяет увидеть два самых долгих теста в файле с тестами.

Таким образом, можно увидеть, какие тесты выполняются дольше всего, и оптимизировать их.

-v

Нажать, чтобы раскрыть
pytest -v

Выполнение команды pytest -v позволяет увидеть более подробную информацию о тестах.

-s

Нажать, чтобы раскрыть
pytest -s

Выполнение команды pytest -s позволяет увидеть вывод тестов в реальном времени.

-l (--showlocals)

Нажать, чтобы раскрыть
pytest -l

Выполнение команды pytest -l позволяет увидеть локальные переменные в тестах.

-rfEX

Нажать, чтобы раскрыть
pytest -rfEX

Выполнение команды pytest -rfEX позволяет увидеть только ошибки и отчет о тестах. А именно failed, expected, xfailed.

Задаем по умолчанию для всех тестов определенные параметры

Для этого в файле pytest.ini нужно добавить следующие строки:

[pytest]
addopts = -v -l --durations=10

Или в файле pyproject.toml если используется poetry в проекте:

[tool.pytest.ini_options]
addopts = "-v -l --durations=10"

Теперь при запуске тестов, будут использоваться параметры -v -l --durations=10 по умолчанию.

Марки

Марки позволяют маркировать тесты, разделять их на группы, управлять отдельными тестами и группами.

Краткий список марок:

skip

  • Марка skip позволяет пропустить тест и не выполнять его содержимое. С помощью аргумента reason следует указывать причину пропуска.
  • Марка skip может быть использована для пропуска тестов, которые еще не готовы к запуску. Или для пропуска тестов, которые не нужно запускать в данный момент.
@pytest.mark.skip()
@pytest.mark.skip(reason="Этот тест еще не завершен")

Также можно делать пропуск теста, если он соответствует определенному условию:

is_macos = True


@pytest.fixture()
def is_macos():
    return True


def test_third(is_macos):
    if is_macos:
        pytest.skip(reason="Не запускается на macos")

skipif

  • Марка skipif позволяет пропустить тест, если выполняется определенное условие. С помощью аргумента condition указывается условие, а с помощью аргумента reason - причина пропуска;
@pytest.mark.skipif(condition=True, reason="Этот тест еще не завершен")

На примере выше, тест будет пропущен, если условие condition=True выполняется, а именно если функция is_macos возвращает True. Если функция возвращает False, то тест будет запущен.

xfail

  • Марка xfail позволяет указать, что тест заведомо может не работать. При этом тест запустится. Причину также указывают с помощью аргумента reason.
@pytest.mark.xfail()

@pytest.mark.xfail(reason="просто потому что")
def test_fail():
    user1 = random.randint(0, 100)
    user2 = random.randint(0, 100)

    assert user1 <= 100
    assert user2 <= 100
    try:
        assert user1 == user2
    except AssertionError:
        pytest.xfail("TASK-1234")

Если тест прошел успешно(исправили к примеру баг), то он будет отмечен как XPASS, если тест упал, то он будет отмечен как XFAIL.

Если на тест навешена марка xfail, и он будет запущен, но он не проверит ошибки синтаксиса внутри теста. То есть, если внутри теста есть ошибки, то они не будут показаны. Чтобы подобного избежать, можно не использовать марку xfail, а использовать try и except:

def test_fail():
    user1 = random.randint(0, 100)
    user2 = random.randint(0, 100)

    assert user1 <= 100
    assert user2 <= 100
    try:
        assert user1 == user2
    except AssertionError:
        pytest.xfail("TASK-1234")

И если в тесте есть ошибки синтаксиса, то они будут показаны.

usefixtures

  • Марка usefixtures позволяет указать, какие фикстуры использовать для теста. С помощью аргумента fixtures указывают, какие фикстуры использовать;
@pytest.mark.usefixtures("fixture_name")

Как навесить марку на весь файл с тестами

Если необходимо навесить марку на весь файл с тестами, то можно использовать следующий код:

import pytest

pytestmark = pytest.mark.skip(reason="TASK-1234 Тест нестабильный потому что время от времени не хватает таймаута")

Как запустить тесты несколько раз

Для запуска тестов несколько раз, можно использовать плагинpytest-repeat. Для этого нужно установить пакет:

pip install pytest-repeat

Если необходимо запускать все тесты несколько раз, то можно добавить в файл pytest.ini следующие строки:

[pytest]
addopts = --count=3

Теперь все тесты будут запускаться три раза.

Если необходимо запустить только определенный тест несколько раз, то можно использовать следующую команду:

pytest --count=3 test_simple.py

Выполнение команды pytest --count=3 test_simple.py позволяет запустить тесты определенное количество раз(В данном случае 3 раза).

Как добавить свои марки

Если вы создаете свои марки, то их пояснение нужно добавить в файл pytest.ini:

[pytest]
markers =
    smoke: smoke tests
    regression: regression tests
    e2e: end-to-end tests

Или в файл pyproject.toml если используется poetry в проекте:

[tool.pytest.ini_options]
markers=[
    "fast: Маркируем тесты длящиеся менее пяти секунд",
    "slow: Тесты, которые длятся больше пяти секунд"
]

Это необходимо для того, чтобы при запуске тестов с марками, pytest понимал, что такие марки существуют. И что они означают.

Параметризация

Параметризация это способ протестировать один и тот же тест с разными входными данными.
Параметризация в pytest осуществляется с помощью декоратора @pytest.mark.parametrize.

Один параметр

@pytest.mark.parametrize("browser", ["Chrome", "Firefox", "Safari"])
def test_with_matrix_param(browser):
    pass

Несколько параметров

@pytest.mark.parametrize("browser, version",
                         [("Chrome", 122.0), ("Firefox", 123.0), ("Safari", 12.3)]
                         )
def test_with_matrix_param(browser, version):
    pass

Несколько параметризаций на один тест

@pytest.mark.parametrize("browser", ["Chrome", "Firefox", "Safari"])
@pytest.mark.parametrize("test_role", ["manager", "guest", "admin"])
def test_with_matrix_param(browser, test_role):
    pass

Переопределение названия параметра в ids

@pytest.mark.parametrize("browser, version",
                         [("Chrome", "12-rc4125"), ("Firefox", 123.0), ("Safari", 12.3)],
                         ids=["Chrome", "Firefox", "Safari"]
                         )
def test_with_matrix_param(browser, version):
    pass

Таким образом мы получаем название как часть имени теста. И мы можем запускать тесты по названию.

pytest -k "Chrome"

Параметры параметризации pytest.param

@pytest.mark.parametrize("browser",
                         [
                             pytest.param("Chrome", id="Chrome"),
                             pytest.param("Firefox", marks=[pytest.mark.slow]),
                             pytest.param("Safari", marks=[pytest.mark.xfail(reason="TASK-123 Safari problem")]),
                         ]
                         )
def test_with_param_marks(browser):
    pass

В данной функции тестирования, мы используем параметризацию с помощью pytest.param.
pytest.param позволяет добавить маркировку к параметрам.

Фикстуры

Фикстура — это функция, которая подготавливает среду перед выполнением теста: создаёт данные, открывает соединения, запускает браузер и т.д.

Фикстуры позволяют:

  • переиспользовать подготовку окружения
  • избегать дублирования кода
  • делать тесты чище и короче
  • легко управлять временем жизни ресурсов
import pytest

@pytest.fixture
def number():
    return 10

def test_value(number):
    assert number == 10

Pytest автоматически передаёт результат фикстуры в тест по имени аргумента.

Области видимости фикстур (scope)

Фикстуры могут жить разное время

scope Когда вызывается Пример использования
function перед каждым тестом создание тестовых данных
class один раз для класса запуск браузера на весь класс
module один раз на файл подключение к API
session один раз на запуск pytest глобальные настройки, авторизация

Для того чтобы указать область видимости фикстуры нужно указать это значение в параметре scope

@pytest.fixture(scope="module")
def db():
    print("connect")
    yield
    print("disconnect")

Фикстура может выполнять код до и после теста. Для этого в ней используют yield.

  • всё, что написано до yield, выполняется как подготовка (setup)
  • в момент yield управление передаётся тесту
  • если после yield указано значение, оно будет передано в тест
  • после завершения теста управление возвращается в фикстуру, и выполняется код после yield (teardown) — в любом случае, даже если тест упал с ошибкой
@pytest.fixture
def resource():
    print("setup")
    yield "value"
    print("teardown")

Использование:

def test_example(resource):
    assert resource == "value"

Также вернуть какое-либо значение из фикстуры в тест можно с помощью return, тогда фикстура будет ввести себя как обычная функция, то есть при использовании return нельзя выполнить код после теста (teardown) внутри этой же фикстуры. Если нужен teardown — используйте yield.

Autouse

Если фикстура должна срабатывать без указания в тесте, используют autouse=True.

@pytest.fixture(autouse=True)
def prepare():
    print("Выполняется перед тестом")

Такие фикстуры можно применять в:

  • логирование
  • очистка окружения
  • подготовка временных директорий
  • включение нужных настроек

**Хранение фикстур

Если фикстуры нужны многим тестам — их помещают в файл conftest.py, в таком случает фикстуры становятся доступны во всех тестах в этой директории и поддиректориях и их не нужно импортировать вручную.

Если фикстура нужно только в одном файле ее можно хранить в нем.

В случае если по каким-то причинам не хочется их хранить в conftest.py или в файле с тестом то можно создать отдельный файл и подключать фикстуру через импортирование или через плагины pytest

# preparing_fixtures.py
@pytest.fixture
def turn_on_capcha():
    turn_captcha_mode"on")


# conftest.py 
pytest_plugins = [
    "fixtures.page_fixtures",
    "fixtures.component_fixtures",
    "fixtures.preparing_fixtures",
]

При подключении через плагины фикстуры будут доступны для всего проекта

Параметризация фикстур

@pytest.fixture(params=["Chrome", "Firefox", "Safari"])
def browser(request):
    if request.param == "Chrome":
        return ""
    if request.param == "Firefox":
        return ""
    if request.param == "Safari":
        return ""


def test_with_parametrized_fixture(browser):
    pass

В данном примере, мы параметризуем фикстуру browser. request.param - это параметр, который мы передаем в фикстуру. А из фикстуры мы получаем значение, через request.

Indirect параметризация

@pytest.fixture(params=["Chrome", "Firefox", "Safari"])
def browser(request):
    if request.param == "Chrome":
        return ""
    if request.param == "Firefox":
        return ""
    if request.param == "Safari":
        return ""


@pytest.mark.parametrize("browser", ["Chrome"], indirect=True) # переопределяем фикстуру, чтобы запускалось только с Chrome
def test_with_indirect_parametrization(browser):
    pass

В данном примере, мы используем параметризацию фикстуры с помощью indirect=True. Это позволяет передать параметры из фикстуры в тест.

Индиректная параметризация позволяет переопределить фикстуру, чтобы запускалось только с определенным параметром.

Присваивание фикстуры в переменную

@pytest.fixture(params=["Chrome", "Firefox", "Safari"])
def browser(request):
    if request.param == "Chrome":
        return ""
    if request.param == "Firefox":
        return ""
    if request.param == "Safari":
        return ""

    
chrome_only = pytest.mark.parametrize("browser", ["Chrome"], indirect=True)


@chrome_only
def test_chrome_extension(browser):
    pass

В данном примере, мы присваиваем фикстуру в переменную chrome_only. И используем ее в декораторе @chrome_only. Это позволяет запускать тесты только с определенными параметрами и использовать более красивые и читабельные декораторы.

__repr__ в параметризации

@dataclass
class User:
    id: int
    name: str
    age: int
    description: str

    def __repr__(self):
        return f"{self.name} ({self.id})"


user1 = User(id=1, name="Mario", age=32, description="something " * 10)
user2 = User(id=2, name="Wario", age=62, description="else " * 10)


def show_user(user):
    return f"{user.name} ({user.id})"


@pytest.mark.parametrize("user", [user1, user2], ids=show_user)
def test_users(user):
    pass

В данном примере, мы используем параметризацию с помощью дата класса User. Используем функцию show_user для отображения имени и id пользователя в названии теста. И передаем ее в параметр ids. Также метод __repr__ в датаклассе, позволяет получить строковое представление объекта в виде имени и id. И потом использовать его в ids.

Подсказка к домашнему заданию

Смотрите только когда не можете понять что делать(нажать, чтобы раскрыть)
Задание 
1. Пропустите мобильный тест, если соотношение сторон десктопное (и наоборот);

2. Переопределите параметр с помощью indirect;

3. Сделайте разные фикстуры для каждого теста.

Первое задание

Согласно 1 заданию нужно создать фикстуру, которая будет пропускать мобильный тест, если соотношение сторон десктопное (и наоборот).

# conftest.py
@pytest.fixture(params=[(набор размеров экранов)])
def setup_browser(request):
    width, height = request.param
    browser.config.window_width = width
    browser.config.window_height = height
    if width > 900:
        yield "desktop"
    else:
        yield "mobile"

    browser.quit()

# test_github_skip.py
def test_mobile_skip(setup_browser):
    if setup_browser == "mobile":
        pytest.skip("Это мобилное разрешение")
    browser.open("https://github.com/")
    ...

def test_desktop_skip(setup_browser):
    if setup_browser == "desktop":
        pytest.skip("Это десктопное разрешение")
    browser.open("https://github.com/")
    ...
Второе задание

Согласно 2 заданию нужно переопределить параметр с помощью indirect.

# conftest.py
@pytest.fixture(params=[(набор размеров экранов)])
def desktop_browser(request):
    width, height = request.param
    browser.config.window_width = width
    browser.config.window_height = height
    yield
    browser.quit()

@pytest.fixture(params=[(набор размеров экранов)])
def mobile_browser(request):
    width, height = request.param
    browser.config.window_width = width
    browser.config.window_height = height
    yield
    browser.quit()


# test_github_indirect.py
@pytest.mark.parametrize("desktop_browser", [(набор размеров экранов)], indirect=True)
def test_desktop_indirect(desktop_browser):
    browser.open("https://github.com/")
    ...

@pytest.mark.parametrize("mobile_browser", [(набор размеров экранов)], indirect=True)
def test_mobile_indirect(mobile_browser):
    browser.open("https://github.com/")
    ...
Третье задание Согласно 3 заданию нужно создать разные фикстуры для каждого теста.
# conftest.py
@pytest.fixture(params=[(набор размеров экранов)])
def desktop_browser(request):
    width, height = request.param
    browser.config.window_width = width
    browser.config.window_height = height
    yield
    browser.quit()

@pytest.fixture(params=[(набор размеров экранов)])
def mobile_browser(request):
    width, height = request.param
    browser.config.window_width = width
    browser.config.window_height = height
    yield
    browser.quit()

# test_github_fixture.py
def test_desktop_fixture(desktop_browser):
    browser.open("https://github.com/")
    ...
    
def test_mobile_fixture(mobile_browser):
    browser.open("https://github.com/")
    ...

Содержание
Командная строка
Кодировка UTF-8 в Java
Список полезных книг для автоматизаторов тестирования на языке Java
Список полезных книг для автоматизаторов тестирования на языке Python Структура проекта Github README.md

Java:
1. Вводное занятие. Сразу к практике.
2. Git. GitHub. Погружаемся.
3. Погружаемся в инструментарий и библиотеки
4. Основы Java
5. Продолжаем разрабатывать автотесты. PageObjects
6. JUnit 5
7. Allure Reports
8. Работа с файлами
9. Selenide #1
10. Jenkins. Создаем первую задачу
11. Управляем параметрами в коде и в Jenkins
12. Отправляем уведомления о результатах прохождения автотестов
13. Учимся быстро разрабатывать проекты для тестовых заданий
14. Selenoid
15. Библиотека Owner
16. REST API. Пишем автотесты с Rest assured
17. REST API. Декомпозируем UI тесты. Подключаем отчетность
18. REST API. Продолжаем изучать
19. Мобильная автоматизация #1. Разрабатываем автотесты с Browserstack
20. Allure TestOps
21. Переезд на собственную инфраструктуру Java

Python:
1. Вводное занятие. Сразу к практике!
2. Погружаемся в инструментарий и библиотеки
3. Git. GitHub. Погружаемся.
4. Основы Python
5. Selene #1
6. Основы Python. Часть II
7. Основы Python. Часть III
8. Page Object
9. Allure Reports
10. Работаем с файлами Python
11. Jenkins. Создаем первую задачу и управляем параметрами Python
12. Телеграм бот. Отправляем уведомления о результатах прохождения тестов
13. Pytest
14. Selenoid
15. Pytest. Часть II
16. Venv, Poetry и управление зависимостями проекта
17. REST API. Часть I. Пишем автотесты с Requests
18. REST API. Часть II. Продолжаем изучать
19. Мобильная автоматизация #1. Разрабатываем автотесты с Browserstack
20. Мобильная автоматизация #2. Разрабатываем автотесты с эмулятором Android устройства
21. Allure TestOps
22. Переезд на собственную инфраструктуру Python

Clone this wiki locally