Примеры unit test python

Модуль unittest: тестируем свои программы

Python 3 логотип

Представьте, что вы написали какую-либо программу, а теперь хотите проверить, правильно ли она работает. Что вы для этого сделаете? Скорее всего, вы запустите её несколько раз с различными входными данными, и убедитесь в правильности выдаваемого ответа.

А теперь вы что-то поменяли и снова хотите проверить корректность программы. Запускать ещё несколько раз? А если потом снова что-то поменяется? Нельзя ли как-то автоматизировать это дело?

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

Для автоматизации тестов, unittest поддерживает некоторые важные концепции:

  • Испытательный стенд (test fixture) — выполняется подготовка, необходимая для выполнения тестов и все необходимые действия для очистки после выполнения тестов. Это может включать, например, создание временных баз данных или запуск серверного процесса.
  • Тестовый случай (test case) — минимальный блок тестирования. Он проверяет ответы для разных наборов данных. Модуль unittest предоставляет базовый класс TestCase, который можно использовать для создания новых тестовых случаев.
  • Набор тестов (test suite) — несколько тестовых случаев, наборов тестов или и того и другого. Он используется для объединения тестов, которые должны быть выполнены вместе.
  • Исполнитель тестов (test runner) — компонент, который управляет выполнением тестов и предоставляет пользователю результат. Исполнитель может использовать графический или текстовый интерфейс или возвращать специальное значение, которое сообщает о результатах выполнения тестов.

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

Вот короткий скрипт для тестирования трех методов строк:

  Тестовый случай создаётся путём наследования от unittest.TestCase. 3 отдельных теста определяются с помощью методов, имя которых начинается на test. Это соглашение говорит исполнителю тестов о том, какие методы являются тестами.

Суть каждого теста — вызов assertEqual() для проверки ожидаемого результата; assertTrue() или assertFalse() для проверки условия; assertRaises() для проверки, что метод порождает исключение. Эти методы используются вместо обычного assert для того, чтобы исполнитель тестов смог взять все результаты и оформить отчёт.

Методы setUp() и tearDown() (которые в данном простом случае не нужны) позволяют определять инструкции, выполняемые перед и после каждого теста, соответственно.

Последние 2 строки показывают простой способ запуска тестов. unittest.main() предоставляет интерфейс командной строки для тестирования программы. Будучи запущенным из командной строки, этот скрипт выводит отчёт, подобный этому:

unittest может быть использован из командной строки для запуска модулей с тестами, классов или даже отдельных методов:

python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method

Можно также указывать путь к файлу:

python -m unittest tests/test_something.py

С помощью флага -v можно получить более детальный отчёт:

python -m unittest -v test_module

Для нашего примера подробный отчёт будет таким:

 -b ( --buffer ) - вывод программы при провале теста будет показан, а не скрыт, как обычно.

-c ( --catch ) — Ctrl+C во время выполнения теста ожидает завершения текущего теста и затем сообщает результаты на данный момент. Второе нажатие Ctrl+C вызывает обычное исключение KeyboardInterrupt.

-f ( --failfast ) — выход после первого же неудачного теста.

--locals (начиная с Python 3.5) — показывать локальные переменные для провалившихся тестов.

Обнаружение тестов

unittest поддерживает простое обнаружение тестов. Для совместимости с обнаружением тестов, все файлы тестов должны быть модулями или пакетами, импортируемыми из директории верхнего уровня проекта (см. подробнее о правилах наименования модулей ).

Обнаружение тестов реализовано в TestLoader.discover(), но может быть использовано из командной строки:

-v ( --verbose ) — подробный вывод.

-s ( --start-directory ) directory_name — директория начала обнаружения тестов (текущая по умолчанию).

-p ( --pattern ) pattern — шаблон названия файлов с тестами (по умолчанию test*.py).

-t ( --top-level-directory ) directory_name — директория верхнего уровня проекта (по умолчанию равна start-directory ).

Организация тестового кода

Базовые блоки тестирования это тестовые случаи — простые случаи, которые должны быть проверены на корректность.

Тестовый случай создаётся путём наследования от unittest.TestCase.

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

Простейший подкласс TestCase может просто реализовывать тестовый метод (метод, начинающийся с test). Вымышленный пример:

Заметьте, что для того, чтобы проверить что-то, мы используем один из assert\*() методов.

Тестов может быть много, и часть кода настройки может повторяться. К счастью, мы можем определить код настройки путём реализации метода setUp(), который будет запускаться перед каждым тестом:

Мы также можем определить метод tearDown(), который будет запускаться после каждого теста:
Можно разместить все тесты в том же файле, что и сама программа (таком как widgets.py), но размещение тестов в отдельном файле (таком как test_widget.py) имеет много преимуществ:
  • Модуль с тестом может быть запущен автономно из командной строки.
  • Тестовый код может быть легко отделён от программы.
  • Меньше искушения изменить тесты для соответствия коду программы без видимой причины.
  • Тестовый код должен изменяться гораздо реже, чем программа.
  • Протестированный код может быть легче переработан.
  • Тесты для модулей на C должны быть в отдельных модулях, так почему же не быть последовательным?
  • Если стратегия тестирования изменяется, нет необходимости изменения кода программы.

Пропуск тестов и ожидаемые ошибки

unittest поддерживает пропуск отдельных тестов, а также классов тестов. Вдобавок, поддерживается пометка теста как «не работает, но так и надо».

Пропуск теста осуществляется использованием декоратора skip() или одного из его условных вариантов.

     Классы также могут быть пропущены:
Ожидаемые ошибки используют декоратор expectedFailure():
Очень просто сделать свой декоратор. Например, следующий декоратор пропускает тест, если переданный объект не имеет указанного атрибута:
 Декораторы, пропускающие тесты или говорящие об ожидаемых ошибках:

@unittest.skip(reason) — пропустить тест. reason описывает причину пропуска.

@unittest.skipIf(condition, reason) — пропустить тест, если condition истинно.

@unittest.skipUnless(condition, reason) — пропустить тест, если condition ложно.

@unittest.expectedFailure — пометить тест как ожидаемая ошибка.

Для пропущенных тестов не запускаются setUp() и tearDown(). Для пропущенных классов не запускаются setUpClass() и tearDownClass(). Для пропущенных модулей не запускаются setUpModule() и tearDownModule().

Различение итераций теста с помощью подтестов

Когда некоторые тесты имеют лишь незначительные отличия, например некоторые параметры, unittest позволяет различать их внутри одного тестового метода, используя менеджер контекста subTest().

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

Модуль unittest предоставляет множество функций для самых различных проверок:

assertTrue(x) — bool(x) is True

assertFalse(x) — bool(x) is False

assertIsNot(a, b) — a is not b

assertIsNotNone(x) — x is not None

assertNotIn(a, b) — a not in b

assertIsInstance(a, b) — isinstance(a, b)

assertNotIsInstance(a, b) — not isinstance(a, b)

assertRaises(exc, fun, *args, **kwds) — fun(*args, **kwds) порождает исключение exc

assertRaisesRegex(exc, r, fun, *args, **kwds) — fun(*args, **kwds) порождает исключение exc и сообщение соответствует регулярному выражению r

assertWarns(warn, fun, *args, **kwds) — fun(*args, **kwds) порождает предупреждение

assertWarnsRegex(warn, r, fun, *args, **kwds) — fun(*args, **kwds) порождает предупреждение и сообщение соответствует регулярному выражению r

assertAlmostEqual(a, b) — round(a-b, 7) == 0

assertNotAlmostEqual(a, b) — round(a-b, 7) != 0

assertGreaterEqual(a, b) — a >= b

assertNotRegex(s, r) — not r.search(s)

assertCountEqual(a, b) — a и b содержат те же элементы в одинаковых количествах, но порядок не важен

Для вставки кода на Python в комментарий заключайте его в теги

  • Книги о Python
  • GUI (графический интерфейс пользователя)
  • Курсы Python
  • Модули
  • Новости мира Python
  • NumPy
  • Обработка данных
  • Основы программирования
  • Примеры программ
  • Типы данных в Python
  • Видео
  • Python для Web
  • Работа для Python-программистов

Источник

Читайте также:  Меняем цвет шрифта при помощи HTML
Оцените статью