Многие считают что язык программирования Python − это просто. Такое впечатление складывается после прочитанной книги по Python, статьи или видео-туториала. Возможно, он действительно проще, чем другие технологии, вот только без трудностей не бывает даже тут. Но и их можно избежать, если понять принципы тестирования Python-кода.
Сразу к делу. Вот как будет проходить проверка функции sum() (1,2,3) равна шести:
Тест не выведет ничего на REPL, так как значения верны. Но если результат sum() неверен, это приведет к ошибке AssertionError и сообщению “Should be 6”.
В REPL вы видите AssertionError, потому что результат не соответствует 6. Переместите код в новый файл, названный test_sum.py и выполните снова:
Вы написали пример теста, утверждение и точку входа.
sum() принимает любое повторяющееся значение в качестве первого аргумента. Вы проверили список, теперь проверьте так же и tuple. Создайте новый файл test_sum_2.py:
Когда вы выполняете test_sum_2.py, скрипт выдает ошибку, так как sum() от (1,2,2) не равна 6:
Для более масштабных вещей используют running tests. Это специальные приложения для запуска тестов, проверки вывода и предоставления инструментов для отладки и диагностики тестов и приложений.
Unittest
Unittest содержит как структуру тестирования Python, так и test runners. У него есть несколько требований:
- Нужно помещать свои тесты в классы как методы.
- Нужно использовать ряд специальных методов утверждения в unittest − TestCase вместо assert.
Для преобразования в unittest:
- Импортируйте его из стандартной библиотеки.
- Создайте класс TestSum, который наследуется от класса TestCase.
- Преобразуйте тестовые функции в методы путем добавления self в качестве первого аргумента.
- Изменить утверждение на использование метода self.assertEqual() в классе TestCase.
- Изменить точку входа в командной строке для вызова unittest.main().
- Создайте test_sum_unittest.py:
Совместим с любыми тестами, написанными с использованием unittest. Чтобы начать тестирование Python-кода, установите его из PyPl и выполните в командной строке. Он попытается обнаружить все скрипты с именем test*.py, наследующие от unittest.
Pytest
Pytest также поддерживает выполнение тестов unittest, а его преимущество заключается в написании своих тестов. Они представляют собой ряд функций в файле Python.
Кроме того, он отличается:
- Поддержкой встроенного утверждения assert вместо использования специальных методов self.assert*().
- Возможностью повторного запуска с пропущенного теста.
- Наличием системы дополнительных плагинов.
Написание тестового примера TestSum для pytest будет выглядеть так:
Если вы только начали изучать Python с нуля, обязательно затроньте и темы дебага/тестирования. Понимание принципов тестирования Python включает в себя принципы написания собственных тестов. Создайте новую папку проекта и внутри нее, под названием my_sum, еще одну. Внутри my_sum создайте пустой файл с именем __init__.py:
Откройте my_sum/__init__.py и создайте новую функцию sum(), которая обрабатывает повторения.
В этом коде создается переменная с именем total, которая повторяет все значения в arg и добавляет их к total.
Где писать тест
Создайте в корне файл test.py, который будет содержать ваш первый тест:
Как структурировать простой тест?
Прежде чем перейти к написанию тестов, вы должны понять следующее:
- Что вы хотите проверить?
- Вы пишете unit test или integration test?
После убедитесь, что структура теста соответствует следующему порядку:
- Создание структуры ввода.
- Выполнение кода и определение вывода.
- Сравнивание полученного с ожидаемым результатом.
Для этого приложения вы должны проверить sum(). Есть много вариантов поведения функции, которые нужно учитывать:
- Может ли функция суммировать целые числа?
- Может ли она использовать set или tuple?
- Что происходит, когда вы вводите неверное значение, например, переменную или целую строчку?
- Что происходит, когда значение отрицательно?
Начнем с суммы целых чисел.
Код импортирует sum() из папки my_sum, затем определяет новый класс теста TestSum, наследуемый от unittest, а TestCase определяет тестовый метод .test_list_int() для проверки списка целых чисел.
Метод .test_list_int() будет:
- Описывать переменные списка чисел.
- Назначать результат my_sum.sum(data) для результирующей переменной.
- Проверять, что значение равно шести, используя метод .assertEqual() в классе unittestTestCase.
- Определять точку ввода в командную строку, где выполняется unittest test–runner .main().
Как писать утверждения и проверки assertions
Последним этапом теста является проверка вывода на основе известного ответа. Это называется утверждением − assertion. Есть несколько общих принципов их написания:
- Удостоверьтесь, что тесты могут повторяться.
- Попробуйте проверять результаты, которые относятся к входным данным, например, проверка результата суммы значений в sum().
Unittest поставляется со множеством методов для проверки значений и переменных. Вот некоторые из наиболее используемых:
Это точка входа в командную строку. Она означает, что если вы выполните скрипт самостоятельно, запустив python.test.py в командной строке, он вызовет unittest.main(), после чего запустятся все классы, которые наследуются от unittest.TestCase в этом файле.
Вы можете предоставить дополнительные опции для изменения вывода. Один из них – “–v”:
Вместо предоставления имени модуля, содержащего тесты, можно запросить автоматическое обнаружение:
Если у вас есть несколько тестов, и вы следуете шаблону test*.py, можно указать имя каталога, используя –s flag:
Если исходный код отсутствует в корне каталога и содержится в подкаталоге, можно сообщить Unittest, где выполнить тесты, чтобы он правильно импортировал модули с –t flag:
Результаты тестирования
sum() должна иметь возможность принимать другие списки числовых типов (дроби).
В верхней части файла test.py добавьте оператор импорта:
Добавьте тест с утверждением, ожидающим неправильное значение. В этом случае ожидание sum() от (¼, ¼ и ⅖) будет равно 1.
Если вы снова выполните тест с python –m unittest test, вы увидите следующее:
Выполнение тестов в PyCharm
Если вы используете PyCharm IDE, вы можете запустить Unittest или pytest, выполнив следующие шаги:
- В окне инструментов проекта выберите каталог тестов
- В контекстном меню выберите команду запуск для Unittest.
Выполнение тестов из кода Visual Studio
Если у вас установлен плагин Python, вы можете настроить конфигурацию своих тестов, открыв командную палитру с помощью Ctrl+Shift+P и набрав «Python test»:
Выберите Debug All Unit Tests. VSCode выдаст подсказку для настройки тестовой среды. Нажмите на шестеренку, чтобы выбрать unittest и домашний каталог.
Как использовать Django Test Runner
Шаблон startapp в Django создаст файл test.py внутри каталога приложений. Если его нет, создайте:
Основное отличие состоит в том, что наследовать нужно от django.test.TestCase вместо unittest.TestCase. Эти классы имеют один и тот же API, но Django TestCase устанавливает все необходимое для тестирования.
Чтобы выполнить свой тестовый пакет вместо использования unittest в командной строке, используйте метод manage.py:
Если вы нуждаетесь в нескольких тестовых файлах, замените test.py на папку с именем test, поместите внутрь пустой файл с именем __init__.py и создайте файлы test_*.Py. Django обнаружит и выполнит их.
Как использовать unittest и Flask
Flask требует, чтобы приложение было импортировано и установлено в тестовом режиме. Можно создать копию тестового клиента и использовать его для запросов приложения.
Все экземпляры тестового клиента выполняются в методе setUp. В следующем примере my_app − имя приложения.
Ранее, когда мы делали список сценариев для проверки sum(), возник вопрос: что происходит при вводе неверного значения? Тест провалится.
Существует способ обработки ожидаемых ошибок. Можно использовать .assertRaises() в качестве контекстного менеджера, а затем выполнить тест внутри блока:
Теперь этот тест будет пройден только если sum(data) вызовет TypeError. Позже условие можно будет изменить.
Структура
Существуют и побочные эффекты: они усложняют тестирование, поскольку при каждом выполнении результаты могут разниться.
- Реструктурирование кода.
- Использование способа mocking для методов функции.
- Использование integration test вместо unit test.
Написание integration tests
До этого времени мы занимались в основном unit testing. Двигаемся дальше.
Integration testing – тестирование нескольких компонентов приложения для проверки их совместной работоспособности. Integration testing может требовать разные сценарии работы:
- Вызов HTTP REST API
- Вызов Python API
- Вызов веб–службы
- Запуск командной строки
Каждый из этих типов integration tests может быть записан так же, как и unit test. Существенное отличие состоит в том, что Integration tests проверяют сразу несколько компонентов. Можно разделить тесты на integration и unit − разбить их по папкам:
Можно указать путь к тестам:
Тестирование data-driven приложений
Многие integration tests требуют базовые данные, содержащие определенные значения. Например, может потребоваться тест, который проверяет правильность отображения приложения с более чем 100 клиентами в базе данных, написанной на японском.
Хорошим решением будет хранение тестовых данных в отдельной папке под названием «fixtures», чтобы указать, где именно содержится нужная информация.
Вот пример этой структуры, если данные состоят из файлов JSON:
В тесте можно использовать метод .setUp() для загрузки тестовых данных из файла. Помните, что у вас может быть несколько тестов в одном файле Python, и unittest discovery будет выполнять их все. Для каждого набора тестовых данных может быть один тестовый пример:
До сих пор вы работали только с одной версией Python, используя виртуальную среду с определенным набором зависимостей. Tox − приложение, которое автоматизирует процесс тестирования Python в нескольких средах.
Установка Tox
Настройка Tox для ваших нужд
Tox настраивается через файл конфигурации в каталоге проекта. Он содержит следующее:
- Команда запуска для выполнения тестов
- Дополнительные пакеты, необходимые для выполнения
- Разные версии Python для тестирования
Вместо изучения синтаксиса конфигурации Tox, можно начать с использования приложения быстрого запуска:
Средство конфигурации Tox создаст файл, похожий на следующий в tox.ini:
Прежде чем запустить Tox, нужно создать файл setup.py, который будет содержать порядок установки пакета.
Вместо этого, можно добавить строку в файл tox.ini в заголовке [tox]:
Если вы не будете создавать файл setup.py, но ваше приложение зависит от PyPl, вам нужно указать это в нескольких строках в разделе [testenv]. Например, для Django потребуется следующее:
Теперь можно запустить Tox и создать две виртуальные среды: одну для Python 2.7 и одну для Python 3.6. Каталог Tox называется .tox/. Внутри него Tox выполнит обнаружение python – m unittest для каждой виртуальной среды.
Этот процесс также можно запустить, вызвав Tox в командной строке. На этом заканчиваем рассказ о принципах тестирования Python-кода.
Python сделал тестирование доступным: unittest и собственные методы позволяют качественно тестировать код.
По мере развития навыков, можете постепенно перейти к использованию pytest и других более продвинутых функций.