Как запускать тесты
Способы запуска тестов: из командной строки, автообнаружение и программный запуск.
Test discovery — механизм, которым unittest сам находит все файлы с тестами по соглашению об именах (
test_*.py).
Запуск из командной строки
В реальном проекте тесты лежат в отдельных файлах и запускаются командой. Базовый способ — указать модуль с тестами:
# Запустить тесты из файла test_calc.py
python -m unittest test_calc
# Запустить конкретный класс
python -m unittest test_calc.TestAdd
# Запустить один конкретный тест
python -m unittest test_calc.TestAdd.test_positive
Флаг -v (verbose) включает подробный вывод — по строке на тест, как наш verbosity=2:
python -m unittest -v test_calc
Автообнаружение тестов
Когда тестов много, перечислять файлы вручную неудобно. Команда discover сама обходит проект и собирает все тесты:
# Найти и запустить все test_*.py в текущей папке и подпапках
python -m unittest discover
# Указать стартовую папку и шаблон имён
python -m unittest discover -s tests -p "test_*.py"
Чтобы автообнаружение работало, придерживайтесь соглашений: файлы называются test_*.py, классы наследуются от TestCase, методы начинаются с test_. Это стандарт, которого ждёт инструмент.
Программный запуск (то, что работает прямо в браузере)
Иногда нужно запустить тесты из самого Python-кода — например, в ноутбуке, в обучающей среде или внутри скрипта. Для этого есть два пути.
Способ 1: unittest.main
import unittest
def is_palindrome(s):
s = s.lower()
return s == s[::-1]
class TestPalindrome(unittest.TestCase):
def test_true(self):
self.assertTrue(is_palindrome("шалаш"))
def test_false(self):
self.assertFalse(is_palindrome("питон"))
# argv=[''] — не разбирать аргументы; exit=False — не завершать процесс
unittest.main(argv=[''], exit=False, verbosity=2)
Вывод:
test_false (__main__.TestPalindrome.test_false) ... ok test_true (__main__.TestPalindrome.test_true) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
Способ 2: TextTestRunner и доступ к результату
Этот способ даёт объект с результатом, у которого можно спросить статус программно:
import unittest
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(1 + 1, 2)
def test_mul(self):
self.assertEqual(2 * 3, 6)
# Собираем набор тестов из класса
suite = unittest.TestLoader().loadTestsFromTestCase(TestMath)
result = unittest.TextTestRunner(verbosity=2).run(suite)
print("Успешно:", result.wasSuccessful())
print("Запущено тестов:", result.testsRun)
Вывод: (строки отчёта unittest идут в stderr, а print — в stdout; устойчивая часть)
Ran 2 tests in 0.000s OK Успешно: True Запущено тестов: 2
Метод result.wasSuccessful() возвращает True/False — удобно для скриптов и CI, где по статусу решают, продолжать сборку или нет.
Памятка по запуску
| Команда | Что делает |
python -m unittest | запускает обнаруженные тесты |
python -m unittest discover | ищет все test_*.py |
python -m unittest -v | подробный вывод (по строке на тест) |
unittest.main(argv=[''], exit=False) | программный запуск внутри кода |
Где в проекте лежат тесты
Сложилось две распространённые раскладки. Первая — отдельная папка tests/ рядом с кодом, куда складывают все файлы test_*.py; именно её обходит discover. Вторая — тестовый файл рядом с тестируемым модулем (calc.py и test_calc.py в одной папке). Обе работают; для небольших проектов проще вторая, для крупных — отдельная папка tests/. Главное — единообразие и соблюдение префикса test_, иначе автообнаружение не найдёт файлы.
Как читать отчёт о прогоне
В конце прогона unittest печатает сводку: сколько тестов выполнено (Ran N tests), за какое время и итог — OK либо FAILED с разбивкой (failures=..., errors=...). При подробном выводе перед сводкой идёт по строке на каждый тест со статусом ok/FAIL/ERROR/skipped. Привыкайте сразу смотреть в конец отчёта: финальная строка отвечает на главный вопрос — «всё зелёное или нет». В CI именно эта строка определяет, прошла сборка или упала.
Итог
- В проектах тесты запускают командой
python -m unittest, флаг-v— подробнее. discoverсам находит файлыtest_*.py— соблюдайте соглашения об именах.- Программно:
unittest.main(argv=[''], exit=False)илиTextTestRunner().run(suite). result.wasSuccessful()даёт статус для скриптов и CI.