← Назад к вопросам
Как выявить ошибку в функции при тестировании?
2.0 Middle🔥 191 комментариев
#Тестирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как выявить ошибку в функции при тестировании?
Ощибки в функциях выявляются на разных уровнях тестирования. Вот комплексный подход:
1. Unit-тесты с assertions
Основной метод — писать тесты с явными проверками результатов:
import pytest
def calculate_discount(price: float, percentage: float) -> float:
"""Вычисляет цену со скидкой"""
return price * (1 - percentage / 100)
def test_calculate_discount():
# Проверяем базовый случай
assert calculate_discount(100, 10) == 90.0
# Проверяем граничные случаи
assert calculate_discount(100, 0) == 100.0
assert calculate_discount(100, 100) == 0.0
# Проверяем отрицательные значения
with pytest.raises(ValueError):
calculate_discount(-10, 10)
2. Параметризованные тесты
Для тестирования множества входных данных:
@pytest.mark.parametrize("price,discount,expected", [
(100, 10, 90),
(200, 50, 100),
(50, 0, 50),
(1000, 100, 0),
])
def test_calculate_discount_parametrized(price, discount, expected):
assert calculate_discount(price, discount) == expected
3. Mock и Patch для изоляции ошибок
Когда функция зависит от внешних ресурсов (БД, API):
from unittest.mock import patch, MagicMock
def get_user_email(user_id: int) -> str:
"""Получает email пользователя из БД"""
user = fetch_from_db(user_id)
return user.email if user else None
def test_get_user_email_with_mock():
# Мокируем функцию fetch_from_db
with patch('module.fetch_from_db') as mock_fetch:
mock_fetch.return_value = MagicMock(email='user@example.com')
result = get_user_email(1)
assert result == 'user@example.com'
mock_fetch.assert_called_once_with(1)
def test_get_user_email_not_found():
# Тестируем случай, когда пользователя нет
with patch('module.fetch_from_db', return_value=None):
result = get_user_email(999)
assert result is None
4. Проверка исключений
Неправильная обработка ошибок — частая проблема:
def parse_integer(value: str) -> int:
"""Парсит строку в число с валидацией"""
try:
return int(value)
except ValueError:
raise ValueError(f"Cannot parse '{value}' as integer")
def test_parse_integer_valid():
assert parse_integer("42") == 42
def test_parse_integer_invalid():
with pytest.raises(ValueError, match="Cannot parse"):
parse_integer("not_a_number")
5. Проверка побочных эффектов
Иногда функция имеет побочные эффекты (изменение состояния, логирование):
from unittest.mock import patch, call
class UserService:
def __init__(self, logger):
self.logger = logger
def activate_user(self, user_id: int):
user = self.get_user(user_id)
user.active = True
self.save(user)
self.logger.info(f"User {user_id} activated")
def test_activate_user_logs_event():
# Проверяем, что логирование произошло
mock_logger = MagicMock()
service = UserService(logger=mock_logger)
with patch.object(service, 'get_user'), \
patch.object(service, 'save'):
service.activate_user(1)
mock_logger.info.assert_called_once()
assert "activated" in mock_logger.info.call_args[0][0]
6. Coverage для выявления непокрытого кода
Найди код, который не выполняется в тестах:
# Запусти тесты с отчётом о покрытии
pytest --cov=module --cov-report=html
# Проверь, какие строки не покрыты
# Если есть ошибка в коде, но он не в тестах — coverage это выявит
7. Integration tests для сложных сценариев
Если ошибка проявляется только при взаимодействии компонентов:
def test_user_creation_workflow():
"""Полный сценарий создания пользователя"""
# 1. Создаём пользователя
user = create_user(name="John", email="john@example.com")
assert user.id is not None
# 2. Проверяем, что он в БД
fetched = get_user(user.id)
assert fetched.name == "John"
# 3. Обновляем
fetched.name = "Jane"
save_user(fetched)
# 4. Проверяем обновление
updated = get_user(user.id)
assert updated.name == "Jane"
8. Отладка во время тестирования
Если тест падает, используй pdb:
def test_complex_logic():
result = complex_function()
# Запускает интерактивный отладчик
breakpoint()
assert result == expected
# Или через команду:
pytest -s -k test_complex_logic --pdb
9. Property-based тестирование
Для поиска краевых случаев, которые ты не видишь:
from hypothesis import given, strategies as st
@given(st.integers(), st.integers())
def test_addition_commutative(a, b):
"""Проверяет, что a+b = b+a для всех целых чисел"""
assert a + b == b + a
Рекомендации:
- Unit-тесты — первая линия защиты для отдельных функций
- Параметризация — экономит время и выявляет граничные случаи
- Mock — изолирует ошибки от зависимостей
- Coverage — показывает, какие части кода не протестированы
- Integration тесты — выявляют ошибки взаимодействия
- Property-based tests — находят неожиданные края
Комбинация всех этих методов обеспечивает надёжное выявление ошибок ещё до production.