Введение в тестирование кода с помощью pytest
PyTest is a popular testing library for Python. It is a simple, easy-to-use testing framework that allows you to write and execute tests for your Python code. Here is a brief introduction to using PyTest

Тест - это код, который выполняет код. Когда вы начинаете разрабатывать новую функцию для своего проекта Python, вы можете формализовать требования к ней в виде кода. Поступая таким образом, вы не только документируете то, как должен использоваться код вашей реализации, но и можете автоматически запускать все тесты, чтобы всегда быть уверенным, что ваш код соответствует вашим требованиям. Одним из таких инструментов, который помогает вам в этом, является pytest
, и это, вероятно, самый популярный инструмент тестирования во вселенной Python.
Все вокруг assert
Предположим, что вы написали функцию, которая проверяет адрес электронной почты. Обратите внимание, что мы упрощаем задачу и не используем регулярные выражения или проверку DNS для проверки адресов электронной почты. Вместо этого мы просто убедимся, что в проверяемой строке есть только один знак @
и только латинские символы, цифры, а также символы .
, -
и _
.
|
|
Теперь у нас есть некоторые утверждения в нашем коде. Например, мы утверждаем, что эти адреса электронной почты действительны:
С другой стороны, мы ожидаем, что наша функция вернет False для таких адресов электронной почты, как:
not [email protected]
- contains spacejohn.doe
missing @ signjohn,[email protected]
contains comma
Мы можем проверить, что наша функция действительно ведет себя так, как мы ожидаем:
|
|
Эти примеры адресов электронной почты, которые мы придумываем, называются тестовыми случаями. Для каждого тестового случая мы ожидаем определенный результат. Такой инструмент, как pytest
, может помочь автоматизировать тестирование этих утверждений. Запись этих утверждений может помочь вам:
- документировать, как будет использоваться ваш код
- убедиться, что будущие изменения не сломают другие части вашего программного обеспечения
- подумать о возможных крайних случаях использования ваших функциональных возможностей.
Чтобы сделать это, мы просто создадим новый файл для всех наших тестов и поместим туда несколько функций.
|
|
Проведение тестов
Простой пример
Итак, у нас есть два файла в каталоге нашего проекта: validator.py
и test_validator.py
.
Теперь мы можем просто запустить pytest
из командной строки. Его вывод должен выглядеть примерно так:
|
|
Здесь pytest
сообщает нам, что он нашел три тестовые функции внутри test_validator.py
и что все эти функции были пройдены (на что указывают три точки ...
).
Индикатор 100%
дает нам хорошее чувство, поскольку мы уверены, что наш валидатор работает так, как ожидалось. Однако, как было сказано во введении, функция валидатора далека от совершенства. Так же как и наши тестовые примеры. Даже без тестирования DNS мы бы отметили адрес электронной почты типа [email protected]
как действительный, в то время как адрес типа [email protected]
был бы отмечен как недействительный.
Давайте теперь добавим эти тестовые случаи в наш test_validator.py
.
|
|
Если мы снова запустим pytest
, то увидим неудачные тесты:
|
|
Обратите внимание, что мы получили два FF
в дополнение к трем ...
точкам, указывающим на то, что две тестовые функции не сработали.
Кроме того, мы получаем новый раздел FAILURES
в выводе, который подробно объясняет, в какой момент наш тест потерпел неудачу. Это очень полезно для отладки.
Designing Tests
Наш небольшой пример с валидатором является подтверждением важности разработки тестов.
Сначала мы написали функцию валидатора, а затем придумали для нее несколько тестовых примеров. Вскоре мы заметили, что эти тесты ни в коем случае не являются исчерпывающими. Напротив, мы упустили некоторые существенные аспекты валидации адреса электронной почты.
Возможно, вы слышали о Test Driven Development (TDD), которая выступает за прямо противоположное: Сначала нужно правильно сформулировать требования, написав тестовые примеры, и не приступать к реализации функции до того, как вы почувствуете, что охватили все тестовые случаи. Такой образ мышления всегда был хорошей идеей, но со временем он приобрел еще большее значение, поскольку сложность программных проектов возросла.
В ближайшее время я напишу еще одну статью в блоге о TDD, чтобы рассказать о нем более подробно.
Конфигурация
Обычно настройка проекта намного сложнее, чем просто один файл с функцией валидатора в нем.
Возможно, у вас есть структура пакета Python для вашего проекта, или ваш код зависит от внешних зависимостей, таких как база данных.
Fixtures
Вы могли использовать термин фикстура в разных контекстах. Например, для Django web framework, фикстуры относятся к коллекции начальных данных, загружаемых в базу данных. Однако, в контексте pytest
, фикстуры относятся только к функциям, выполняемым pytest
до и/или после фактических тестовых функций.
Установка и снос
Мы можем создавать такие функции с помощью декоратора pytest.fixture()
. Пока что мы делаем это внутри файла test_validator.py
.
|
|
Обратите внимание, что создание базы данных и ее разрушение происходит в одном и том же фикстуре. Ключевое слово yield
указывает на ту часть, где pytest
выполняет фактические тесты.
Чтобы фикстура действительно использовалась одним из ваших тестов, вы просто добавляете имя фикстуры в качестве аргумента, как показано ниже (по-прежнему в test_validator.py
):
|
|
Getting Data from Fixtures
Вместо использования yield
, функция приспособления может также возвращать произвольные значения:
|
|
Опять же, запрос этого приспособления из тестовой функции осуществляется путем предоставления имени приспособления в качестве параметра:
|
|
Configuration Files
pytest
может читать свою конфигурацию, специфичную для проекта, из одного из этих файлов:
pytest.ini
tox.ini
setup.cfg
Какой файл использовать, зависит от того, какие другие инструменты вы можете использовать в своем проекте. Если вы упаковали свой проект, вам следует использовать файл setup.cfg
. Если вы используете tox для тестирования вашего кода в различных средах, вы можете поместить конфигурацию pytest
в файл tox.ini
. Файл pytest.ini
можно использовать, если вы не хотите использовать никаких дополнительных инструментов, кроме pytest
.
Конфигурационный файл выглядит одинаково для каждого из этих трех типов файлов:
Using pytest.ini and tox.ini
|
|
Если вы используете файл setup.cfg, единственное отличие заключается в том, что вы должны префикс секции [pytest]
с tool:
, как показано ниже:
|
|
conftest.py
Каждая папка с тестовыми файлами может содержать файл conftest.py
, который читается pytest
. Это хорошее место для размещения ваших пользовательских фикстур, так как они могут быть общими для разных тестовых файлов.
Файл(ы) conftest.py
может(ют) изменять поведение pytest
в зависимости от проекта.
Помимо общих фикстур, вы можете разместить внешние хуки и плагины или модификаторы для PATH
, используемого pytest
для обнаружения тестов и кода реализации.
CLI / PDB
Во время разработки, в основном, когда вы пишете тесты перед реализацией, pytest
может быть полезным инструментом для отладки.
Мы рассмотрим наиболее полезные опции командной строки.
Running Only One Test
Если вы хотите запустить только один конкретный тест, вы можете сослаться на него через файл test_
, в котором он находится, и имя функции:
|
|
Collect Only
Иногда вы просто хотите иметь список тестовой коллекции, а не выполнять все тестовые функции.
|
|
Exit on the first error
Вы можете заставить pytest
прекратить выполнение последующих тестов после неудачного:
|
|
Run the last failed test only
Если вы хотите запустить только те тесты, которые не прошли в прошлый раз, вы можете сделать это с помощью флага --lf
:
|
|
Run all tests, but run the last failed ones first
|
|
Show values of local variables in the output
Если мы создадим более сложную тестовую функцию с некоторыми локальными переменными, мы можем указать pytest
отобразить эти локальные переменные с помощью флага -l
.
Давайте перепишем нашу тестовую функцию следующим образом:
|
|
Тогда,
|
|
даст нам такой результат:
|
|
Using pytest with a debugger
Существует отладчик командной строки, называемый pdb
, который встроен в Python. Вы можете использовать pytest
для отладки кода вашей тестовой функции.
Если вы запустите pytest
с --pdb
, он запустит сессию отладки pdb
сразу после того, как в вашем тесте возникнет исключение. В большинстве случаев это не очень полезно, поскольку вы можете захотеть проверить каждую строку кода, предшествующую возникшему исключению.
Другой вариант - флаг --trace
для pytest
, который будет устанавливать точку останова в первой строке каждой тестовой функции. Это может стать немного неудобным, если у вас много тестов. Поэтому для целей отладки хорошей комбинацией будет --lf --trace
, которая запустит сессию отладки с pdb
в начале последнего неудачного теста:
|
|
CI / CD
В современных программных проектах программное обеспечение разрабатывается в соответствии с принципами Test Driven Development и доставляется через конвейер непрерывной интеграции / непрерывного развертывания, включающий автоматизированное тестирование.
Типичная установка заключается в том, что коммиты в ветку main/master
отклоняются, если не пройдены все тестовые функции.
Если вы хотите узнать больше об использовании pytest
в среде CI/CD, оставайтесь с нами, так как я планирую новую статью на эту тему.
Документация
Официальная документация по pytest
находится здесь: https://docs.pytest.org
Небольшая заметка:
Это перепост оригинальной статьи Баса Штайнса, сделанный с его разрешения. Посетите его сайт, чтобы почитать другие его статьи и/или подпишись на него в Twitter: @bascodes