← Назад к вопросам

Как выявить ошибку в функции при тестировании?

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.