Почему тестировщик не пригласил Pytest на свой день рождения? Потому что он боялся, что тесты падут и вечеринка закончится с ошибкой 404 – "Файл не найден"!

Pytest – это один из наиболее популярных фреймворков для написания тестов на Python. 

Он известен своей простотой в использовании, обширным сообществом и широким спектром функциональности. Однако, даже если вы являетесь опытным пользователем Pytest, вероятно, вы не используете все его функции. 

В этой статье мы рассмотрим топ малоизвестных, но полезных функций Pytest.

1. Множественный запуск тестов

Pytest позволяет запускать тесты несколько раз с использованием параметра --count. Это полезно в следующих сценариях:

  • Поиск непостоянных ошибок.

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

  • Исследование производительности.

    Многократный запуск тестов позволяет оценить производительность нашего кода, обнаружить утечки памяти и другие производственные проблемы.

Иногда при тестировании разработанного программного обеспечения возникают проблемы, связанные с производительностью. Многократный запуск тестов может оказаться полезным инструментом для оценки производительности нашего кода и выявления проблем, которые могут быть вызваны различными факторами, такими как архитектурные особенности, внешние зависимости или динамические условия окружения.

Преимущества множественного запуска тестов для оценки производительности включают:

  • Обнаружение непостоянных проблем 

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

  • Сбор статистики

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

  • Регрессионное тестирование производительности 

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

Пример использования множественного запуска тестов для оценки производительности:

pytest --count=10 performance_test.py

В данном случае, тест performance_test.py будет запущен 10 раз, и результаты будут анализироваться для выявления временных изменений в производительности. Это помогает убедиться, что наше программное обеспечение остается производительным даже при различных условиях и изменениях.

  • Определение стабильности

Многократный запуск также может помочь нам оценить, насколько стабильны тесты и сколько раз они падают из-за нестабильных условий.

pytest --count=100

Допустим, у нас есть тест для функции calculate, и мы хотим убедиться, что он работает стабильно. Мы можем запустить его 10 раз, чтобы проверить, не возникают ли временные ошибки.

Пример:

pytest --count=10 test_calculate.py

Эта команда выполнит тест test_calculate.py 10 раз и предоставит отчет о результатах каждого запуска.

2. Отчеты о покрытии кода

Pytest интегрируется с различными инструментами для анализа покрытия кода, такими как coverage.py. Отчеты о покрытии кода – это важный инструмент для:

  • Измерения качества наших тестов

    Они позволяют определить, какая часть нашего кода осталась непокрытой тестами.

  • Поиска "мертвого" кода

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

  • Оптимизации кода

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

pytest --cov=my_module

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

Пример:

pytest --cov=my_module tests/

Эта команда выполнит тесты из каталога tests/ и создаст отчет о покрытии для модуля my_module. Мы увидим, какие строки кода были покрыты тестами, а какие остались непокрытыми.

3. Параметризация тестов

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

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

import pytest

@pytest.mark.parametrize("input, expected", [(1, 2), (2, 4), (3, 6)])
def test_double(input, expected):
    assert double(input) == expected

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

Допустим, у нас есть функция validate_email, и мы хотим проверить ее с разными входными данными. Мы можем использовать параметризацию для этой цели.

Пример:

import pytest

@pytest.mark.parametrize("email, is_valid", [("test@example.com", True), ("invalid-email", False)])
def test_validate_email(email, is_valid):
    result = validate_email(email)
    assert result == is_valid

Эта команда выполнит тест test_validate_email с разными входными данными, проверяя, корректно ли функция validate_email определяет валидность email.

4. Фильтрация и запуск по маркерам

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

import pytest

@pytest.mark.smoke
def test_smoke_test():
    assert some_function() == 42

Затем мы можем запустить только тесты, помеченные как smoke-тесты:

pytest -m smoke

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

Предположим, у нас есть большой набор тестов, и мы хотим запустить только smoke-тесты, чтобы быстро проверить, работает ли базовая функциональность нашего приложения.

pytest -m smoke запустит только тесты, которые были помечены маркером smoke. Можно использовать этот подход для быстрой проверки основных сценариев нашего приложения перед выпуском.

5. Автоматическое обновление зависимостей

С помощью параметра --self-upgrade, Pytest позволяет автоматически обновлять сам фреймворк до последней версии. Это важно для:

  • Получения последних исправлений ошибок и новых функций.

  • Гарантии совместимости нашего кода с актуальной версией Pytest.

pytest --self-upgrade \

Обновляйте Pytest регулярно, чтобы быть в курсе последних изменений и улучшений.

6. Использование параметров командной строки в тестах

С использованием фикстуры request, мы можем передавать параметры командной строки в наши тесты. Это полезно, когда наши тесты зависят от внешних данных:

def test_command_line_args(request):
    arg_value = request.config.getoption("--my-arg")
    assert some_function(arg_value) == expected_value

Запустим тест с параметром командной строки:

pytest --my-arg=42

Это дает нам гибкость в настройке тестов в зависимости от внешних условий.

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

Пример:

def test_calculate_tax():
    tax_rate = float(input("Введем ставку налога: "))
    income = 1000
    result = calculate_tax(income, tax_rate)
    assert result == income * tax_rate

В этом примере значение ставки налога вводится через командную строку при запуске теста.

Заключение

Pytest – мощный инструмент для написания тестов на Python, и в этой статье мы рассмотрели некоторые из его малоиспользуемых функций. Использование этих функций может сделать наш процесс тестирования более эффективным и удобным. Не стесняйтесь исследовать Pytest и использовать его возможности на полную мощь, чтобы улучшить качество нашего кода.

Комментарии (9)


  1. Andrey_Solomatin
    23.11.2023 13:00

    pytest --cov

    Вы забыли упомянуть, что этого нет в pytest. Это один из плагинов,https://docs.pytest.org/en/7.4.x/reference/plugin_list.html


    1. IPLATONI Автор
      23.11.2023 13:00

      Да, согласен, забыл) Но есть же pip install pytest-cov =)


  1. SomeSmallThings
    23.11.2023 13:00
    +3

    А откуда данные по частоте использования? По моему опыту параметризация (в том числе фикстур) встречается чуть ли не повсеместно, по крайней мере малоиспользуемой уж точно не назовешь


    1. IPLATONI Автор
      23.11.2023 13:00

      На самом деле, многие функции используются чаще с приходом опыта, для меня например параметризация тестов - штука довольно новая)


      1. Andrey_Solomatin
        23.11.2023 13:00
        +1

        для меня например параметризация тестов - штука довольно новая)


        И как это сопоставлять с названием статьи (Самые малоиспользуемые функции Pytest)? Для меня статья явно не оправдывает ожидания, после прочтения заголовка.

        Добавьте в статью что-то про свой опыт. Сколько проектов вы просмотрели по использованию фич, в скольких компаниях?

        У меня выборка не большая и топом будет pytest. Часто его используют для сбора и запуска тестов, а сами тесты пишут по старинке с помощью Unuttest.

        Из того, что для меня полезно, но редко кто использует это запуск доктестов.







        1. IPLATONI Автор
          23.11.2023 13:00
          -3

          Очень жаль, что название не оправдывает ожидания. Но уверен, что найдутся те, для кого оправдает. Если у меня будет время, я напишу статью, которая будет полезна абсолютно всем)
          Unittest и запуск доктестов - это идеи для других статей


      1. SaM1808
        23.11.2023 13:00

        Марки тоже для вас штука новая? Я просто не представляю, как ещё удобнее рулить тестами, если не марками.


    1. gigimon
      23.11.2023 13:00
      +1

      Хочется с вами согласиться, но после собеседования примерно 60 кандидатов использующих pytest, очень мало кто это использует, как и фикстуры (кроме пары встроенных). Проведя столько интервью, стало очень грустно


  1. vodan
    23.11.2023 13:00
    +1

    Использование input внутри теста это путь в никуда... Не стоит это использовать в реальных проектах