Введение
Данный урок открывает череду обучающих материалов на тему "Тестирование в Python". В данном мини-курсе будет рассказано об основных инструментах применяющиеся для тестирования.
Этот урок имеет вводный характер и знакомит с общей информацией о тестировании и простой конструкцией, которая позволяет значительно улучшить Python-код.
Определение
Тестирование программного обеспечения (Software Testing) - проверка соответствия между реальным и ожидаемым поведением программы, осуществляемая при конечном наборе тестов, выбранном определенным образом. [IEEE Guide to Software Engineering Body of Knowledge, SWEBOK, 2004].
В более широком смысле, тестирование - это одна из техник контроля качества, включающая в себя действия по планированию работ (Test Management), проектированию тестов (Test Design), выполнению тестирования (Test Execution) и анализу полученных результатов (Test Analysis).
Для разработчика же интересней следующее определение:
Тестирование — это проверка соответствия программы требованиям, осуществляемая путём наблюдения за её работой в специальных, искусственно созданных ситуациях, выбранных определённым образом.
Хочется обратить внимание на слова “искусственно созданных ситуациях, выбранных определённым образом”. Это означает, что не имеет смысла тестировать вообще все ситуации, стоит выбирать критически важные места и сценарии.
Уровни тестирования
Тестирование для разработчика состоит из написания тестов. Тест – это такой же программный код, который пишется аналогично коду для реализации бизнес-логики. Тесты проверяют сценарии работы программы (test-case).
Тест-кейсы встречаются самые различные, один от другого может резко отличаться. По желанию можно тестировать ВСЕ возможные и невозможные ситуации. Однако стоит соблюдать адекватность и покрывать код тестами ровно настолько, насколько требуется для уверенного понимания, что бизнес-логика работает как задумано.
Например, функция, которая возвращает числовое значение от 0 до 100. В тестах стоит проверить не только правильность значений из этого диапазона, но и то, что других значений не возникает.
Негласное правило – если на участке кода проявилась ошибка, то стоит написать тест на этот случай.
Тестирование смело делится на несколько уровней глубины. Наиболее показательная классификация по уровням тестирования в данном случае такая:
- Системное тестирование – тестирование полностью интегрированного программного приложения
- Интеграционное тестирование – тестирование интегрированных групп программных модулей
- Модульное тестирование или юнит-тестирование – тестирование отдельных модулей исходного кода приложения
Когда говорят "тестирование", не выделяя конкретный тип, то говорят скорее всего о модульном тестировании.
Пойдем с низу вверх (от 3 к 1)
Модульное тестирование
Модульное тестирование, или юнит-тестирование (англ. unit testing) — процесс в программировании, позволяющий проверить на корректность отдельные модули исходного кода программы.
Идея состоит в том, чтобы писать тесты для каждой нетривиальной функции или метода. Это позволяет достаточно быстро проверить, не привело ли очередное изменение кода к регрессии, то есть к появлению ошибок в уже протестированных местах программы и облегчает обнаружение и устранение таких ошибок.
Инструменты
В Python для модульного тестирования применяют
- PyUnit
- PyTest
- Nose
Об этих инструментах будет описано в следующих статьях.
Интеграционное тестирование
Интеграцио́нное тестирование или функциональное тестирование предназначено для проверки связи между компонентами, а также взаимодействия с различными частями системы (операционной системой, оборудованием либо связи между различными системами).
Подходы к интеграционному тестированию
- Снизу вверх (Bottom Up Integration)
- Все низкоуровневые модули, процедуры или функции собираются воедино и затем тестируются. После чего собирается следующий уровень модулей для проведения интеграционного тестирования. Данный подход считается полезным, если все или практически все модули разрабатываемого уровня готовы.
- Сверху вниз (Top Down Integration)
- Вначале тестируются все высокоуровневые модули, и постепенно, один за другим добавляются низкоуровневые. Все модули более низкого уровня симулируются заглушками с аналогичной функциональностью, затем по мере готовности они заменяются реальными активными компонентами.
- Большой взрыв ("Big Bang" Integration)
- Все или практически все разработанные модули собираются вместе в виде законченной системы или ее основной части, и затем проводится интеграционное тестирование. Такой подход очень хорош для сохранения времени. Однако, если тест кейсы и их результаты записаны не верно, то сам процесс интеграции сильно осложнится, что станет преградой для команды тестирования при достижении основной цели интеграционного тестирования
Инструменты
Для автоматизации интеграционного тестирования применяются системы непрерывной интеграции (Continuous Integration System, CIS). Принцип действия таких систем состоит в следующем:
- CIS производит мониторинг системы контроля версий;
- При изменении исходных кодов в репозитории производится обновление локального хранилища;
- Выполняются необходимые проверки и модульные тесты;
- Исходные коды компилируются в готовые выполняемые модули;
- Выполняются тесты интеграционного уровня;
- Генерируется отчет о тестировании.
Примеры инструментов:
- Bamboo
- Hudson и Jenkins
- CruiseControl
- TeamCity
- BuildBot
- Travis CI
- Team Foundation Server
Системное тестирование
Основной задачей системного тестирования является проверка как функциональных, так и не функциональных требований в системе в целом. При этом выявляются дефекты:
- Неверное использование ресурсов системы
- Непредусмотренные комбинации данных пользовательского уровня
- Несовместимость с окружением
- Непредусмотренные сценарии использования
- Отсутствующая или неверная функциональность
- Неудобство использования
- И другие
Для минимизации рисков, связанных с особенностями поведения системы в той или иной среде, во время тестирования рекомендуется использовать окружение максимально приближенное к тому, на которое будет установлен продукт после выдачи.
Стоит выделить два подхода к системному тестированию:
- на базе требований (requirements based). Для каждого требования пишутся тестовые случаи (test cases), проверяющие выполнение данного требования.
- на базе случаев использования (use case based). На основе представления о способах использования продукта создаются случаи использования системы (Use Cases).
По конкретному случаю использования можно определить один или более сценариев. На проверку каждого сценария пишутся тест кейсы (test cases), которые реализуются в виде тестов.
Инструменты
Для описания сценариев можно использовать BDD (behavior-driven development)
- pytest-bdd
- behave
- freshen
Простой инструмент проверки данных – assert
Так как этот урок почти полностью состоит из теории, то разбавлю конструкцией языка, которая помогает писать код и тесты более высокого качества.
Python – язык с динамической типизацией. Это значит, что при написании Python-кода мы явно не указываем тип данных, а во время исполнения не гарантируется явный тип переменной.
В следствии этого возникают сложности. Например, в функции ожидаем целое число, а нам передали list. Это может привести к самым неожиданным ошибками. Чтобы попытаться обойти эту проблему стоит воспользоваться assert.
Assert — это специальная конструкция, позволяющая проверять предположения о значениях произвольных данных в произвольном месте программы.
Assert'ы позволяют отлавливать ошибки в программах на этапе компиляции либо во время исполнения.
Использование конструкции весьма простое:
# -*- encoding: utf-8 -*-
def simple_func(value):
assert type(value) == int
assert value > 0
return value*value
Assert вызывает ошибку, если аргумент равен False. Например так:
>>> print(simple_func(2))
4
>>> print(simple_func(set()))
Traceback (most recent call last):
File "/tmp/testing/app.py", line 11, in <module>
print(simple_func(set()))
File "/tmp/testing/app.py", line 4, in simple_func
assert type(value) == int
AssertionError
>>>
>>> print(simple_func(-1))
Traceback (most recent call last):
File "/tmp/testing/app.py", line 21, in <module>
print(simple_func(-1))
File "/tmp/testing/app.py", line 5, in simple_func
assert value > 0
AssertionError
Traceback показывает строчку, с которой полетело исключение AssertionError, что порой открывает много нового в понимании, как же в реальности работает написанный код.
Выводы
В уроке были кратко описаны виды тестирования: модульное или юнит-тестирование, интеграционное, системное. Для каждого озвучен список инструментов для создания тестов.
В качестве практики описана конструкция assert, позволяющая проверять предположения о значениях произвольных данных в произвольном месте программы.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»