-
Notifications
You must be signed in to change notification settings - Fork 99
13. Pytest
Данный раздел разделен на несколько частей(каждый пункт - это ссылка на соответствующий раздел):
Переход по внутренним ссылкам происходит только при открытом разделе блока в котором находится ссылка.
- Установка Pytest
- Аргументы запуска
- Задаем по умолчанию для всех тестов определенные параметры
- Марки
- Как навесить марку на весь файл с тестами
- Как запустить тесты несколько раз
- Как добавить свои марки
- Параметризация
- Фикструы
- Параметризация фикстур
- Indirect параметризация
- Присваивание фикстуры в переменную
- repr в параметризации
Pytest - это фреймворк для написания тестов на языке Python. Он позволяет писать простые и сложные тесты, а также управлять ими. Pytest позволяет писать тесты в стиле BDD, а также позволяет использовать маркировку тестов, фикстуры и параметризацию.
Для большинства пакетов 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 # выводит только ошибки и отчёт о тестахПримеры использования аргументов:
Нажать, чтобы раскрыть
pytest --co
Если нужно вывести все тесты в определённом файле, то нужно указать путь к файлу(или название файла) после --co:
pytest --co tests/test_example.pyНажать, чтобы раскрыть
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.

Нажать, чтобы раскрыть
pytest -m slow
Команда равноценна
pytest -m "slow"Если необходимо запустить несколько марок, то можно использовать логические операторы and, or, not:
pytest -m "slow or fast"Командной выше будут запущены все тесты, где есть марка slow или fast.

Нажать, чтобы раскрыть
pytest --markers
Нажать, чтобы раскрыть
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 - это фикстура, которая предоставляет временный каталог, уникальный для вызова теста, созданный в базовом временном каталоге.
Нажать, чтобы раскрыть
pytest --setup-planВыполнение команды pytest --setup-plan позволяет увидеть план запуска тестов в проекте.

Если нужно увидеть план запуска тестов определенного файла, то нужно указать название файла и ключ --setup-plan:
pytest test_simple.py --setup-plan 
Нажать, чтобы раскрыть
pytest test_simple.py --durations=2Выполнение команды pytest test_simple.py --durations=2 позволяет увидеть два самых долгих теста в файле с тестами.

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

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

Нажать, чтобы раскрыть
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позволяет пропустить тест и не выполнять его содержимое. С помощью аргумента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позволяет пропустить тест, если выполняется определенное условие. С помощью аргументаconditionуказывается условие, а с помощью аргументаreason- причина пропуска;
@pytest.mark.skipif(condition=True, reason="Этот тест еще не завершен")
На примере выше, тест будет пропущен, если условие condition=True выполняется, а именно если функция is_macos возвращает True. Если функция возвращает False, то тест будет запущен.
- Марка
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позволяет указать, какие фикстуры использовать для теста. С помощью аргумента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
@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.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 == 10Pytest автоматически передаёт результат фикстуры в тест по имени аргумента.
Области видимости фикстур (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.

@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.
Это позволяет запускать тесты только с определенными параметрами и использовать более красивые и читабельные декораторы.

@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/")
...✔️ Сайт
✔️ Чат в Telegram
✔️ Канал в Telegarm
Содержание
Командная строка
Кодировка 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