Тестируем на Python: unittest и pytest. Инструкция для начинающих
Показываем на простом примере, как с помощью unittest и pytest находить ошибки и гарантировать их отсутствие в будущем.
57К открытий69К показов
Меня зовут Андрей Смирнов, я занимаюсь Python-разработкой, автоматизацией технических процессов и преподаю промышленное программирование в Школе программистов МШП.
Не секрет, что разработчики создают программы, которые рано или поздно становятся очень масштабными (если смотреть на количество строчек кода). А с этим приходит и большая ответственность за качество.
Сейчас расскажу, как unittest и pytest помогут найти ошибки в программах и исключить их в будущем.
Итак, тестирование
Каждый, кто писал первые программы (будь то классический «hello, world» или же калькулятор), всегда запускал тесты, чтобы проверить их работу.
Сам факт запуска — самое первое, незримое касание технологии тестирования в вашей жизни. Рассмотрим его как процесс поиска ошибок на чуть более сложной программе.
Например, вам нужно ввести три числа (a, b, c) и найти корни квадратного уравнения. Для решения пишем код:
Сразу оговорюсь: любую задачу, какой бы она ни была краткой, я рассматриваю с позиции «когда-нибудь она вырастет и станет очень объёмной». Поэтому всегда стараюсь разделять программу на различные подпрограммы (ввод/обработка/вывод).
Возможно, вы уже заметили ошибку в коде. Однако иногда она может быть скрыта настолько глубоко, что её просто так не обнаружишь. И в таком случае единственный способ вывести ее на свет — протестировать код. Как это сделать?
— зная алгоритм нахождения корней уравнения, определяем наборы входных данных, которые будут переданы на вход программе;
— зная входные данные, можно вручную просчитать, какой ответ должна дать программа;
— запускаем программу и передаем ей на вход исходные данные;
— получаем от нее ответ и сравниваем с тем, который должен быть получен. Если они совпадают — хорошо, идём к следующему набору данных, если нет, сообщаем об ошибке.
Например, для данной задачи можно подобрать следующие тесты:
- 10x**2 = 0 — единственный корень x=0
- 2x**2 + 5x — 3 = 0 — у такого уравнения два корня (x1 = 0.5, x2=-3)
- 10x**2+2 = 0 — у этого уравнения корней нет
Тесты подобрали, что дальше? Правильно, запускаем:
Упс… В третьем тесте произошла ошибка. Как раз та, которую вы могли заметить в исходном коде программы — не обрабатывался случай с нулевым дискриминантом. В итоге, можно подкорректировать код функции так, чтобы этот вариант обрабатывался правильно:
Запускаем все тесты повторно и они срабатывают нормально.
Но учтите, чтобы повторно проверить программу, потребуется потратить несколько минут и снова проверить все три варианта входных значений. Если таких вариантов будет много, вызывать их вручную будет очень накладно. И здесь на сцену выходит автоматизированное тестирование.
Программа автоматического тестирования запускается на основе заранее заготовленных входных/выходных данных и программы, которая будет их вызывать. По сути, это программа, тестирующая другие программы. И в рамках экосистемы языка Python есть несколько пакетов, позволяющих автоматизировать процесс тестирования.
Unittest и pytest: пишем тесты
Две самые популярные библиотеки — unittest и pytest. Попробуем каждую, чтобы объективно оценить синтаксис.
Начнем с unittest, потому что именно с нее многие знакомятся с миром тестирования. Причина проста: библиотека по умолчанию встроена в стандартную библиотеку языка Python.
Формат кода
По формату написания тестов она сильно напоминает библиотеку JUnit, используемую в языке Java для написания тестов:
- тесты должны быть написаны в классе;
- класс должен быть отнаследован от базового класса unittest.TestCase;
- имена всех функций, являющихся тестами, должны начинаться с ключевого слова test;
- внутри функций должны быть вызовы операторов сравнения (assertX) — именно они будут проверять наши полученные значения на соответствие заявленным.
Пример использования unittest для нашей задачи
Запускается данный код следующей командой
python.exe -m unittest example.py
И в результате на экран будет выведено:
В случае, если в каком-нибудь из тестов будет обнаружена ошибка, unittest не замедлит о ней сообщить:
Unittest: аргументы “за”
- Является частью стандартной библиотеки языка Python: не нужно устанавливать ничего дополнительно;
- Гибкая структура и условия запуска тестов. Для каждого теста можно назначить теги, в соответствии с которыми будем запускаться либо одна, либо другая группа тестов;
- Быстрая генерация отчетов о проведенном тестировании, как в формате plaintext, так и в формате XML.
Unittest: аргументы “против”
- Для проведения тестирования придётся написать достаточно большое количество кода (по сравнению с другими библиотеками);
- Из-за того, что разработчики вдохновлялись форматом библиотеки JUnit, названия основных функций написаны в стиле camelCase (например setUp и assertEqual);
- В языке python согласно рекомендациям pep8 должен использоваться формат названий snake_case (например set_up и assert_equal).
Pytest
Возможно, наиболее популярный фреймворк с открытым исходным кодом из всех, представленных здесь.
Pytest позволяет провести модульное тестирование (тестирование отдельных компонентов программы), функциональное тестирование (тестирование способности кода удовлетворять бизнес-требования), тестирование API (application programming interface) и многое другое.
Формат кода
Написание тестов здесь намного проще, нежели в unittest. Вам нужно просто написать несколько функций, удовлетворяющих следующим условиям:
- Название функции должно начинаться с ключевого слова test;
- Внутри функции должно проверяться логическое выражение при помощи оператора assert.
Пример использования pytest для нашей задачи:
Запускается данный код следующей командой
pytest.exe example.py
И в результате на экран будет выведено:
В случае ошибки вывод будет несколько больше:
Pytest: аргументы “за”
- Позволяет писать компактные (по сравнению с unittest) наборы тестов;
- В случае возникновения ошибок выводится гораздо больше информации о них;
- Позволяет запускать тесты, написанные для других тестирующих систем;
- Имеет систему плагинов (и сотни этих самых плагинов), расширяющую возможности фреймворка. Примеры таких плагинов: pytest-cov, pytest-django, pytest-bdd;
- Позволяет запускать тесты в параллели (при помощи плагина pytest-xdist).
Pytest: аргументы “против”
- pytest не входит в стандартную библиотеку языка Python. Поэтому его придётся устанавливать отдельно при помощи команды pip install pytest;
- совместимость кода с другими фреймворками отсутствует. Так что, если напишете код под pytest, запустить его при помощи встроенного unittest не получится.
Ну и что лучше?
- Если вам нужно базовое юнит-тестирование и вы знакомы с фреймворками вида xUnit, тогда вам подойдёт unittest.
- Если нужен фреймворк, позволяющий создавать краткие и изящные тесты, реализующие сложную логику проверок, то pytest.
Post Scriptum
Тема контроля качества очень обширна. И даже к написанному мной коду очень легко придраться. Как минимум здесь отсутствует проверка на то, что вводимые данные обязательно должны быть целыми числами. Если ввести любое другое число или даже строку, она обязательно завершится с ошибкой.
Кстати, в этой программе я намеренно оставил ещё одну ошибку (на сей раз уже логическую), связанную с нахождением корня. Напишите в комментариях, с чем она может быть связана, и какой тест поможет её отловить ?
57К открытий69К показов