Добавить в корзинуПозвонить
Найти в Дзене
Анастасия Софт

🧪 Юнит-тесты на Python: с чего начать и зачем они вообще нужны?

Когда программист пишет код без тестов, где-то грустит один DevOps-инженер. А если серьёзно — юнит-тесты — это как подушка безопасности: ты надеешься, что они не понадобятся, но они могут спасти проект от полного фиаско. Сегодня разберёмся: Представьте, что вы пекарь. Вы испекли пирог (код) и хотите быть уверены, что он не отравит гостей (пользователей). Юнит-тест — это проба кусочка перед подачей. Он не гарантирует, что всё остальное идеальное, но поможет вовремя заметить, если вы случайно добавили соль вместо сахара. Преимущества юнит-тестов: В Python встроен модуль unittest. Это старый добрый друг, проверенный временем. Написан по образу и подобию JUnit из мира Java. Вот базовая структура теста на unittest: import unittest
# Тестируемая функция
def add(a, b):
return a + b
# Класс с тестами
class TestMathOperations(unittest.TestCase):
def test_add(self):
# Проверяем, что 2 + 3 == 5
self.assertEqual(add(2, 3), 5)
# Запуск тестов
if __name__ == '__main__':
Оглавление

Когда программист пишет код без тестов, где-то грустит один DevOps-инженер. А если серьёзно — юнит-тесты — это как подушка безопасности: ты надеешься, что они не понадобятся, но они могут спасти проект от полного фиаско.

Сегодня разберёмся:

  • зачем вообще нужны юнит-тесты;
  • как пользоваться встроенным модулем unittest;
  • на что тесты не способны (да, у них тоже есть ограничения);
  • типичные ошибки начинающих (и не только) разработчиков;
  • и, конечно, 5 реальных примеров от простого к профессиональному уровню.

🧠 Зачем вообще нужны юнит-тесты?

Представьте, что вы пекарь. Вы испекли пирог (код) и хотите быть уверены, что он не отравит гостей (пользователей). Юнит-тест — это проба кусочка перед подачей. Он не гарантирует, что всё остальное идеальное, но поможет вовремя заметить, если вы случайно добавили соль вместо сахара.

Преимущества юнит-тестов:

  • быстро проверяют, что код работает корректно;
  • позволяют безопасно рефакторить;
  • служат документацией (особенно если код не комментирован, а вы торопились);
  • помогают отловить баги ещё до выката на прод.

🔧 Немного теории: что такое unittest

В Python встроен модуль unittest. Это старый добрый друг, проверенный временем. Написан по образу и подобию JUnit из мира Java.

Вот базовая структура теста на unittest:

import unittest

# Тестируемая функция
def add(a, b):
return a + b

# Класс с тестами
class TestMathOperations(unittest.TestCase):

def test_add(self):
# Проверяем, что 2 + 3 == 5
self.assertEqual(add(2, 3), 5)

# Запуск тестов
if __name__ == '__main__':
unittest.main()

📌 Комментарии по строчкам:

  • import unittest — подключаем библиотеку тестов.
  • def add(a, b) — функция, которую мы тестируем.
  • class TestMathOperations(...) — все тесты пишем внутри наследника unittest.TestCase.
  • def test_add(...) — методы начинаются с test_, иначе unittest их просто проигнорирует.
  • self.assertEqual(...) — проверка: равен ли результат ожидаемому.
  • unittest.main() — запускает все тесты в файле.

✅ Пример 1. Простейшая проверка функции

def is_even(n):
return n % 2 == 0

import unittest

class TestIsEven(unittest.TestCase):

def test_even(self):
self.assertTrue(is_even(2)) # 2 чётное

def test_odd(self):
self.assertFalse(is_even(3)) # 3 нечётное

if __name__ == '__main__':
unittest.main()

Что проверяем:

  • возвращает ли функция True для чётных чисел;
  • False — для нечётных.

👶 Уровень: новичок

📚 Полезно: учит базовому синтаксису unittest.

✅ Пример 2. Проверка исключений

def divide(a, b):
return a / b

import unittest

class TestDivide(unittest.TestCase):

def test_divide_zero(self):
# Проверяем, что при делении на 0 выбрасывается исключение
with self.assertRaises(ZeroDivisionError):
divide(10, 0)

if __name__ == '__main__':
unittest.main()

💥 Здесь мы проверяем, что функция корректно бросает исключение. Без теста кто-то мог бы попытаться делить на ноль в проде. Не смешно, особенно если это банковская система 😅

✅ Пример 3. Работа со списками и проверка порядка

def sort_names(names):
return sorted(names)

import unittest

class TestSortNames(unittest.TestCase):

def test_sorting(self):
unsorted = ['Tom', 'Anna', 'John']
sorted_expected = ['Anna', 'John', 'Tom']
self.assertEqual(sort_names(unsorted), sorted_expected)

if __name__ == '__main__':
unittest.main()

🧩 Здесь важно не только содержание, но и порядок элементов. assertEqual — сравнит списки поэлементно.

✅ Пример 4. Мокаем внешние зависимости

Теперь уровень повыше — симулируем поведение внешней функции (например, API-запроса), не вызывая её на самом деле.

import requests

def get_status_code(url):
response = requests.get(url)
return response.status_code

Теперь тест:

import unittest
from unittest.mock import patch

class TestStatusCode(unittest.TestCase):

@patch('requests.get')
def test_status_code(self, mock_get):
# Подменяем результат вызова requests.get
mock_get.return_value.status_code = 200
self.assertEqual(get_status_code('http://example.com'), 200)

if __name__ == '__main__':
unittest.main()

🕵️ Здесь мы используем patch — это как надеть маску на requests.get и сказать: «Сегодня ты возвращаешь 200, не дергайся!».

✅ Пример 5. Проверка бизнес-логики

Сложный пример: расчёт зарплаты с налогами.

def calculate_salary(gross_salary, tax_percent):
if gross_salary < 0:
raise ValueError("Salary cannot be negative")
tax = gross_salary * (tax_percent / 100)
return gross_salary - tax

Тесты:

class TestSalaryCalculation(unittest.TestCase):

def test_normal_case(self):
self.assertAlmostEqual(calculate_salary(1000, 20), 800)

def test_zero_tax(self):
self.assertEqual(calculate_salary(1000, 0), 1000)

def test_negative_salary(self):
with self.assertRaises(ValueError):
calculate_salary(-500, 20)

if __name__ == '__main__':
unittest.main()

📌 Используем assertAlmostEqual — когда работаете с float-значениями, это безопаснее, чем assertEqual.

❌ Что юнит-тесты НЕ покрывают

  • Они не проверяют интеграцию между компонентами.
  • Не заменяют ручное и UI-тестирование.
  • Не спасут, если требования меняются каждую пятницу вечером.

🧨 Типичные ошибки

  1. Не запускать тесты. Вы удивитесь, как часто это случается.
  2. Тестировать слишком много. Не надо в одном тесте проверять 10 сценариев — лучше разнести.
  3. Отсутствие изоляции. Если тест зависит от другого теста — это путь в ад.
  4. Не использовать моки. Внешние сервисы — ненадёжные союзники.

💡 Когда писать юнит-тесты?

  • Перед тем как выкатить новый функционал.
  • Когда фиксите баг — сначала пишем тест, который воспроизводит баг.
  • При рефакторинге — убедиться, что ничего не сломалось.

TDD (Test Driven Development) — сначала тест, потом код. Не всем подходит, но попробовать стоит.

🔚 Вывод

Писать юнит-тесты — это не скучная обязанность, а возможность спать спокойно ночью и не бояться deploy’а. Даже простые тесты уже повышают устойчивость вашего проекта.

И помните: лучше сто плохо написанных тестов, чем ни одного. Хотя нет, лучше десять хороших 😉

🧪 Юнит-тесты на Python: с чего начать и зачем они вообще нужны?
🧪 Юнит-тесты на Python: с чего начать и зачем они вообще нужны?