Python Тестирование

Pytest – базовые сведения и использование

Конспект по книге – Python Testing with Pytest (Brian Okken)

pytest – это библиотека для тестирования кода.

Главные правила именования:

  • Все файлы с тестами создаются в директории tests.
  • Файлы с тестами именуются по правилу test_… или …_test
  • Функции тестов именуются по правилу test_…
  • Классы для тестов именуются по правилу Test…

Запуск тестов

  1. Из корневой директории
    pytest – запустит все найденные тесты, включая тесты в корневой директории и в директории tests.
  2. Из директории с тестами (может быть любая директория, в которой лежат тесты).
    cd – переход в нужную директорию
    pytest
  3. Запуск отдельного файла с тестами
    pytest test_file.py
  4. Запуск отдельного теста в конкретном файле
    pytest test_file.py::test_function
  5. Запуск тестов из определенного класса
    pytest -v tests/test_1.py::TestUpdate – после двух двоеточий идет название класса Можно также запустить только один метод в данном классе pytest -v tests/test_1.py::TestUpdate::test_bad_id
  6. Запуск с ключами (ключей довольно много, стоит смотреть документацию)
    pytest -v – выведет подробные сведения о тестах pytest -m ‘smoke’ – выполнятся только тесты, помеченные декоратором @pytest.mark.smoke
  7. Запуск тестов по названию
    pytest -v -k _raises – запустятся только тесты, в названии которых есть строка _raises Можно пользоваться операторами and и not для управления запуском по имени
    pytest -v -k “_raises and not delete” – запустятся только тесты, в названии которых есть строка _raises и нет слова delete
  8. При запуске параметризованных тестов (когда задано много разных параметров для одного и того же теста,
    можно запустить тест с конкретными параметрами из существующих

pytest -v tests/test_1.py::test_square[4-16-16]

  1. pytest –tb=no #Скрывает стэк-трейс теста
  2. pytest –lf # Запустятся только тесты, которые зафейлились при последнем запуске.
  3. pytest -l # Покажет в консоли значения всех локальных переменных – полезно при поиске проблемы.
  4. pytest –ff # Сначала запустятся тесты, которые упали при последнем запуске. Затем все остальные.
  5. pytest –cache-show # Смотрим содержимое кэша – там хранится информация об упавших тестах.
  6. pytest –clear-cache # Очистка содержимого кэша
  7. pytest –setup-show # В результатах в консоли будет показано, когда и где срабатывала фикстура.

ФИКСТУРЫ

Фикстура – это функция (декоратор), необходимая для запуска функционала, предшествующего тесту,
и функционала, следующего за тестом.
Фикстуры также удобно использовать для хранения тестовых данных (data fixture). То есть
они возвращают какие-либо данные и больше ничего не делают.

Фикстуры обычно прописывают в файле conftest.py
Cинтаксис при создании фикстуры:

@pytest.fixture()
def function_1():
    Код, выполняемый до теста (открыть браузер, создать данные и т.д.). Это SETUP фикстуры.
    yield # это место соответствует выполнению теста
    Код, выполняемый после теста (закрыть браузер, удалить данные и т.д.) Это TEARDOWN фикстуры.

В файле с тестами фикстура передается как аргумент в нужные тестовые функции:

def test_ui(function_1):
    Код функции

При этом их не надо импортировать, так как фикстуры автоматически подгружаются из conftest.py
Сначала проверяется текущий файл с тестом (фикстура может быть записана прямо в нем,
затем текущая директория, затем более общая и т.д. Первый ближайший файл conftest.py
будет источником фикстур для данного теста.

Фикстуры могут быть самописные (см. выше) и встроенные (их желательно знать – см. ниже).

Если нужно, чтобы фикстура работала только с конкретным тестом, лучше прописать ее прямо в файле
с этим тестом.

При запуске тестов с фикстурами может быть полезно увидеть, как именно работает фикстура.
Для этого есть флаг –setup-show
При запуске это выглядит так:
pytest –setup-show
В результатах в консоли будет показано, когда и где срабатывала фикстура.

ПРИЁМЫ ПРИМЕНЕНИЯ ФИКСТУР

  1. В фикстурах можно хранить тестовые данные, как в примере ниже:
@pytest.fixture()
def a_tuple():
"""Return something more interesting."""
return 1, 'foo', None, {'bar': 23} # возвращает кортеж с нужными нам данными

def test_a_tuple(a_tuple):
"""Demo the a_tuple fixture."""
assert a_tuple[3]['bar'] == 32 # проверяем точность данных из фикстуры
  1. Одну фикстуру можно передавать в другую фикстуру как аргумент.
    Это полезно, например, при создании тестовых данных (их содержит первая фикстура),
    а затем надо эти данные удалить (это делает вторая фикстура). Первая фикстура
    передается аргументом во вторую.

Определение Fixture Scope

Scope – англ. Объём.
Scope – это опциональный параметр фикстуры, контролирующий частоту запуска и закрытия фикстуры.
Это настройка именно фикстуры, не зависящая от функций и классов, к которым она применяется.

Параметр scope может иметь следующие значения: function (default), class, module, session.

Синтаксис:

@pytest.fixture(scope='class')

scope=’function’
Включен по умолчанию, даже если не указан.
Фикстура запускается заново для каждой функции.

scope=’class’
Фикстура запускается заново для каждого класса – и не важно, сколько в классе функций.

scope=’module’
Фикстура запускается заново для каждого модуля, независимо от числа тестовых функций и методов
или других фикстур в данном модуле.

scope=’session’
фикстура запускается один раз за сессию.

Взаимная зависимость фикстур с разными настройками параметра scope

Фикстуры зависят от настроек scope других фикстур в том же скоупе или из скоупа более высокого
ранга (класса, модуля или сессии).

Определение фикстур с помощью usefixtures

Чтобы фикстура применилась к тесту, её нужно отдать в функцию в виде аргумента.
Но можно просто пометить тест или класс декоратором

@pytest.fixture.usefixture('fixture_1', 'fixture_2'…)

чтобы перечисленные в скобках фикстуры применились к нему.

Нет особого смысла применять usefixture к функциям, но это очень удобно по отношению
к классам.

Например, фикстура class_scope применится к нижележащему классу и всем его методам:

@pytest.mark.usefixtures('class_scope')
class TestSomething():
    """Demo class scope fixtures."""

    def test_3(self):
    """Test using a class scope fixture."""

    def test_4(self):
    """Again, multiple tests are more fun."""

autouse

Если нужно, чтобы фикстура срабатывала всегда, используют параметр autouse=True
Пример:

@pytest.fixture(autouse=True, scope='session')
def footer_session_scope():
    """Report the time at the end of a session."""
    yield
    now = time.time()
    print ( 'finished : {}' .format(time.strftime('%d%b%X', time.localtime(now))))

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

Переименование фикстур – аналог алиасов

При передаче фикстуры в качестве аргумента в тестовую функцию обычно используется
имя функции фикстуры. Но мы можем дать специфическое имя любой фикстуре с помощью
параметра name. Пример:

@pytest.fixture(name='lue')
def ultimate_answer_to_life_the_universe_and_everything():
"""Return ultimate answer."""
return 42

def test_everything(lue):
"""Use the shorter name."""
assert lue == 42

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

Передавать параметры можно не только тестам, но и фикстурам.

tasks_to_try = (здесь кортеж из параметров)
task_ids = [здесь список из названий этих параметров]

@pytest.fixture(params=tasks_to_try, ids=task_ids)
def a_task(request):
    return request.param # фикстура возвращает поочередно каждый из элементов из tasks_to_try

Разумеется, эту фикстуру тоже надо передать в нужный тест как параметр.

Встроенные фикстуры – стр. 107

Некоторые фикстуры встроены прямо в pytest. Как правило, это широко применяемые фикстуры.
Встроенные фикстуры – это расширения функциональности pytest.
Встроенные фикстуры позволяют:

  • взаимодействовать с опциями командной строки
  • коммуницировать между тестовыми сессиями и работать с кэшем
  • взаимодействовать с файловой системой и управлять ею
  • валидировать выходящие потоки
  • менять переменные окружения и управлять ими
  • работать с warnings и управлять ими

tmpdir, tmpdir_factory – стр. 108
Создаёт временную директорию в файловой системе перед запуском тестов.
Удаляет созданную директорию сразу после завершения тестов.
Удобна для хранения временных файлов, создаваемых при работе тестов.
Рекомендуется использовать эту фикстуру, если необходимо проверить функционал,
который читает, записывает или изменяет файлы.
tmpdir – создает директорию для одного теста (имеет scope=function).
tmpdir_factory – создает директорию для множества тестов (имеет scope=session).

pytestconfig – стр. 112
Эта фикстура контролирует запуск тестов через аргументы и опции командной строки,
файлы конфигурации, плагины и директории, из которых запускаются тесты.
Фикстура pytestconfig — это ярлык для request.config.

Работа с кэшем – стр. 115
Обычно тестировщики стараются делать тесты максимально независимыми друг от друга.
Мы хотим быть уверенными, что зависимости не зависят от порядка выполнения тестов.
Мы хотим запускать тесты в любом порядке и воспроизводить тот же результат.
Мы также стремимся, чтобы поведение в одной сессии не зависело от поведения в предыдущей.
Однако, иногда информация из предыдущей сессии может быть полезна для текущей сессии.
Для таких случаев полезно использовать фикстуру для работы с кэшем.
Она сохраняет информацию о текущей сессии и передает её в следующую сессию.
Пример использования кэша – это применение флагов –last-failed и –failed-first.
Согласно pytest –help:

–lf, –last-failed rerun only the tests that failed at the last run (or
all if none failed)
–ff, –failed-first run all tests but run the last failures first. This
may re-order tests and thus lead to repeated fixture setup/teardown
–cache-show show cache contents, don’t perform collection or tests
–cache-clear remove all cache contents at start of test run.

ПРИЕМ!!! – стр. 119 – использование кэша для создания фикстуры, измеряющей время выполнения теста.

capsys – стр 123
Позволяет получать данные выходящего потока (stdout и stderr).
По сути, эта фикстура управляет выводом информации из теста, и может временно отключать захват вывода.

monkeypatch – стр. 125
Позволяет динамически модифицировать класс или модуль при запуске тестов.
Может работать в контексте отдельного теста.
Эта фикстура может:

  • назначать и удалять атрибуты
  • устанавливать и удалять переменные окружения
  • работать с путями в операционной системе
  • менять текущую рабочую директорию
    В книге есть пример подмены переменной пути в разных операционных системах с помощью monkeypatch.

doctest_namespace – стр. 129
Добавляет удобства в работе с кодом в докстринг.

recwarn – стр. 133
Используется для проверки варнингов, сгенерированных тестами.
Работает аналогично pytest.warns()

ПЛАГИНЫ – стр. 137

Это всевозможные расширения и дополнения для pytest. Часто это просто удобные фикстуры,
hook-функции, прописанные в conftest.py, написанные кем-то для своих нужд и выложенные в общий доступ.
Ссылки на разные плагины для pytest – стр. 138.

Установка плагинов обычно происходит через pip install…
Ниже дается пример локальной установки:

pip install --no-index --find-links=./some_plugins/ pytest-cov
    Здесь --no-index - не подключаться к сайту PyPI.
    А --find-links=./some_plugins/ - искать плагин в данной директории.

Пример добавления новой опции для командной строки – стр. 144.

Создание своего устанавливаемого плагина – стр. 146.
Вообще, описан процесс создания своей программы для распространения!!!
Описана структура проекта и заполнение файла setup.py с данными программы.

Тестирование плагинов (и других тестовых инструментов) с помощью плагина pytester – стр. 150
Он приложен к pytest, но по умолчанию отключен.

КОНФИГУРАЦИИ – стр. 158.

Речь идет о файлах, непосредственно не являющихся тестовыми.
Они помогают регулировать работу pytest.

pytest.ini – первичный конфигурационный файл pytest, позволяющий влиять на
дефолтное поведение библиотеки.

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

init.py – если положить этот файл в каждую директорию, это позволит
создавать идентичные имена тестовых файлов в разных директориях.
То есть можно использовать одинаковые имена файлов в разных директориях.
И это не будет приводить к ошибкам (называется коллизия).

tox.ini – файл аналогичен pytest.ini, но предназначен для tox (библиотека).

setup.cfg – файл в .ini формате, который влияет на поведение setup.py

Формат данных для pytest.ini

[pytest]
adopts = -rsxX -l --tb=short --strict ///добавляем опции для командной строки, чтобы не вводить их каждый раз.
xfail_strict = true
… more options …

Типично для отладки тестов добавление в pytest.ini опции управления реранами (повторным выполнением кода при падении тестов):

[pytest]
adopts = --reruns 0    # чтобы отключить рераны - 0, или задать числом

Как получить список валидных опций для .ini файлов?

pytest –help

Некоторые плагины могут добавлять свои опции в .ini файлы.

Маркеры

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

[pytest]
markers =
smoke: Run the smoke test functions for tasks project
get: Run the test functions that test tasks.get()

Посмотреть все имеющиеся маркеры можно командой
pytest –markers
Маркеры, не зарегистрированные в pytest.ini здесь не отобразятся.

При запуске тестов с маркерами стоит использовать опцию –strict –tb=line
чтобы неправильно прописанные и незарегистрированные маркеры отобразились как ошибки.
(–strict надо добавить в опции в pytest.ini – см. выше).
pytest –strict –tb=line

Определение наиболее ранней версии pytest

Чтобы ограничить тесты от запуска в слишком ранних версиях библиотеки, стоит добавить в
pytest.ini:

[pytest]
minversion = 3.0

Запуск на более ранних версиях будет падать с ошибкой.

Управление обходом директорий

В pytest директории обходятся рекурсивно. Есть два способа не дать библиотеке обходить директорию:

  1. Именовать директорию, начиная с точки, например: .egg
  2. Прописать в pytest.ini инструкцию вида:
    [pytest]
    norecursedirs = .* venv src *.egg dist build

Для фиксации одной тестовой директории, без необходимости прописывать полный путь
при запуске вложенных в нее тестов (они могут быть в дополнительных вложенных директориях),
стоит прописать в pytest.ini следующую команду:
[pytest]
testpaths = tests
Адреса всех тестовых файлов будут привязаны к директории tests.

Изменение правил обхода директорий и файлов в pytest

Дефолтные правила таковы:

  • обход идет в текущей директории или ищется директория tests, или запускается файл,
    указанный в командной строке: pytest
  • директория и поддиректории обходятся рекурсивно
  • тестовый модуль должен начинаться test_ или заканчиваться на _test.py
  • тестовые функции в тестовых модулях должны начинаться с test_
  • классы должны начинаться с Test, а их методы с test_ и не должны иметь init метода.

Однако, эти правила можно менять в pytest.ini

[pytest]
python_classes = Test Test *Suite
Разрешаем называть наши классы не только Test…, но и …Suite. Например, DeleteSuite.

[pytest]
python_files = test_* test check
В дополнение к стандартному имени тестового модуля, мы можем теперь называть наши тесты
check_…. Например, check_login.py и т.д.

[pytest]
python_functions = test_* check_*
Мы также можем иначе именовать тестовые функции. Здесь разрешаем называть их check_….
Например, check_login_page.

Использование pytest с другими инструментами – стр. 176

Jenkins CI – стр. 197
Позволяет развернуть тесты и нужную инфраструктуру на виртуальной машине (локально или удаленно),
и прогонять тесты после каждого обновления.
Но необходимо научиться правильно все настроить.
С помощью плагинов можно значительно увеличить возможности для анализа тестов:
– добавить нужные названия
– добавить графики и отчеты в виде таблиц

Unittests – стр. 203
Обычно используется для тестирования кода, но часто применяется и для тестов в QA.

tox – стр. 193
Инструмент командной строки для прогона тестов на разных окружениях.
Обладает множеством других полезных возможностей.

Марки – это декораторы, позволяющие управлять запуском тестов.
Можно так пометить тестовые функции маркерами, чтобы одни тесты запускались, а другие нет.
Или чтобы одни тесты запускались до того, как запускаются другие.
Можно задать условие запуска теста или пропустить тест (в том числе по какому-то условию).
Есть марки, ожидающие, что тест упадет, и т.д.

Синтаксис:
В файле с тестами перед соответствующим тестом указать
@pytest.mark.skip # Пропуск теста
def test_function():
pass

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

[pytest]
markers =
smoke: Runs smoke tests
get: Gets testing data

ИСПОЛЬЗОВАНИЕ assert

Можно проверять как равенство, так и неравенство разных переменных и выражений.

assert a == b
assert a + b == b + a
assert a < b assert b >= c + d
assert a != b

Можно вызывать исключения и проверять их:

with pytest.raises(ValueError) as ve:
….код, вызывающий появление исключения….
exception_message = ve.value.args[0]
assert exception_message == ‘Текст исключения’

ПАРАМЕТРИЗАЦИЯ ТЕСТОВ

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

Синтаксис
@pytest.mark.parametrize(argnames, argvalues)

Пример

@pytest.mark.parametrize(("side", "area", "perimeter"), # сначала идет кортеж с названиями параметров
[(4, 16, 16), # далее - три варианта значений - список кортежей
(1.51, 2.28, 6.04),
(1000, 1000000, 4000)],
ids=["Create the Square with a small integer side", # это названия для каждой комбинации - будут выведены в консоль после запуска и отработки теста
"Create the Square with a small fractional side",
"Create the Square wit a large integer side"]
)

def test_square(side, area, perimeter): # это сама функция - в комментарии перечислены параметры из декоратора - аннотация
"""
The function checks the area and the perimeter of a square with different side length
:param side:
:param area:
:param perimeter:
:return:
"""
r = Square(side)
assert r.name == f"Square {side}"
assert r.get_area() == area
assert r.get_perimeter() == perimeter

Можно запустить этот тест лишь с одним набором параметров из существующих:

pytest -v tests/test_1.py::test_square[4-16-16]

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

@pytest.mark.parametrize('task', tasks_to_try, ids=task_ids)
class TestAdd():
    """Demonstrate parametrize and test classes"""

    def test_equivalent(self, task):
        """Similar test, just within a class."""
        task_id = tasks.add(task)
        t_from_db = tasks.get(task_id)
        assert equivalent(t_from_db, task)

    def test_valid_id(self, task):
        """We can use the same data or multiple tests."""
        task_id = tasks.add(task)
        t_from_db = tasks.get(task_id)
        assert t_from_db.id == task_id

ТЕРМИНОЛОГИЯ PYTEST

setup, teardown – часть кода фикстуры, срабатывающая до теста (до yield в фикстуре) и часть кода,
срабатывающая после теста (после yield в фикстуре).

ПОЛЕЗНЫЕ ФИЧИ

Поиск по документации pytest можно делать с помощью:

pytest –help | grep junit(или что-то другое) # Для mac, linux

pytest –help | findstr junit(или другое) # Для Windows

После grep/findstr пишем слово или фразу, которые ищем.

Вставить формулу как
Блок
Строка
Дополнительные настройки
Цвет формулы
Цвет текста
#333333
Используйте LaTeX для набора формулы
Предпросмотр
\({}\)
Формула не набрана
Вставить