Пишете код без тестов? Готовьтесь часами искать баги! Разбираем, как unittest спасет ваше время и нервы.
❓Что такое тестирование?
Простыми словами: это проверка, что ваш код работает правильно. Большинство делает это через print() и запуск в терминале. Но есть способ лучше!
💡 Зачем вообще нужны тесты?
🔸 Экономия времени
Вместо часов отладки — мгновенное обнаружение проблем
🔸 Лучший код
Заставляет продумывать входы, выходы и граничные случаи
🔸 Переиспользование
Копируете функцию? Копируйте и тесты!
🔸 TDD возможен
Можно даже писать тесты ДО написания кода (test-driven development)
Типы тестов:
🔸 Модульное (Unit)
Проверка отдельных функций/компонентов
🔸 Интеграционное
Проверка взаимодействия всех частей
Сегодня фокус на модульном!
📝 Простейший пример:
def add(x, y):
return x + y
# Базовый тест
assert add(2, 4) == 6, "Should be 6"
Если результат не 6 → получите AssertionError ❌
Unittest: профессиональный подход
Встроенный в Python тестировщик. Без установки пакетов!
Структура проекта:
project/
├── functions.py
└── tests.py
functions.py:
def add(x, y):
return x + y
tests.py:
import unittest
from functions import add
class TestAdd(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 4), 6)
self.assertEqual(add(-1, 1), 0)
self.assertEqual(add(-1, -1), -2)
if __name__ == '__main__':
unittest.main()
Запуск:
python tests.py
Результат:
...
-------------------------------
Ran 1 test in 0.001s
OK
Каждая точка (.) = успешный тест ✅
E = ошибка ❌
F = провал теста ❌
🔸Основные assert методы:
🔸 assertEqual(a, b) — a == b
🔸 assertNotEqual(a, b) — a != b
🔸 assertTrue(x) — x is True
🔸 assertFalse(x) — x is False
🔸 assertIn(a, b) — a in b
🔸 assertIsNone(x) — x is None
🔸 assertRaises(Exception) — проверка исключений
🔸 assertAlmostEqual(a, b) — для float чисел
🔸Правила хороших тестов:
1️⃣ Правильные имена
# ✅ Работает
def test_add():
pass
# ❌ НЕ работает (не начинается с test_)
def add_test():
pass
Название ДОЛЖНО начинаться с test_!
2️⃣ От простого к сложному
Сначала очевидные случаи:
def test_add_positive():
self.assertEqual(add(2, 3), 5)
Потом граничные:
def test_add_zero():
self.assertEqual(add(0, 5), 5)
self.assertEqual(add(5, 0), 5)
3️⃣ Тестируйте edge cases
Что может сломать ваш код?
🔸Отрицательные числа
🔸Нули
🔸Очень большие значения
🔸 Пустые строки
🔸 None значения
def test_edge_cases():
self.assertEqual(add(-1, 1), 0)
self.assertEqual(add(0, 0), 0)
self.assertEqual(add(-5, -3), -8)
4️⃣ Независимость тестов
Каждый тест — отдельная вселенная!
class TestCalculator(unittest.TestCase):
def setUp(self):
# Выполняется ПЕРЕД каждым тестом
self.calc = Calculator()
def tearDown(self):
# Выполняется ПОСЛЕ каждого теста
self.calc = None
5️⃣ Избегайте assertTrue
❌ Плохо:
self.assertTrue(add(2, 3) == 5)
# Вывод: Expected True, but was False
✅ Хорошо:
self.assertEqual(add(2, 3), 5)
# Вывод: AssertionError: 5 != 6
Второй вариант даёт НАМНОГО больше информации!
🔸Реальный пример бага:
def round_plus_ten(x):
return round(x) + 10
class TestRound(unittest.TestCase):
def test_round(self):
self.assertEqual(round_plus_ten(4.3), 14) # ✅
self.assertEqual(round_plus_ten(4.7), 15) # ✅
self.assertEqual(round_plus_ten(4.5), 15) # ❌ FAIL!
Почему упало?
round(4.5) = 4, а не 5! Python округляет к ближайшему чётному.
🟧Урок: тесты находят неочевидные баги!
🔸 Продвинутые фишки:
Группировка тестов:
class TestAddition(unittest.TestCase):
def test_positive(self): ...
def test_negative(self): ...
class TestMultiplication(unittest.TestCase):
def test_basic(self): ...
Пропуск тестов:
@unittest.skip("Пока не работает")
def test_future_feature(self):
passПроверка исключений:
def test_divide_by_zero(self):
with self.assertRaises(ZeroDivisionError):
divide(10, 0)
