Как спроектировать функцию для легкого юнит-тестирования

«Как спроектировать функцию для легкого юнит-тестирования» — вопрос из категории Тестирование, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для создания легко тестируемых функций следует придерживаться нескольких ключевых принципов:

  1. Чистота (Purity): Функция не должна иметь побочных эффектов (не изменять глобальные переменные, не писать в файлы/БД). Её результат должен зависеть только от входных аргументов.

  2. Принцип единственной ответственности (Single Responsibility): Функция должна выполнять одну, чётко определённую задачу. Это упрощает её понимание и тестирование.

  3. Явные зависимости (Explicit Dependencies): Всё, что нужно функции для работы (данные, сервисы, конфигурации), должно передаваться ей в качестве аргументов. Этот подход называется Dependency Injection.

Сравнение подходов

Плохой пример (неявная зависимость от БД): Функция жестко связана с внешним источником данных, что делает её тестирование сложным. Для проверки логики потребуется реальное или тестовое подключение к базе.

# Трудно тестировать, так как функция связана с БД
def get_user_report():
    db_connection = connect_to_database()
    user_data = db_connection.fetch_user_data()
    # ...сложная логика формирования отчета
    return f"Report for {user_data['name']}"

Хороший пример (зависимость передается явно): Функция получает уже готовые данные. Её можно протестировать в полной изоляции, передав любой тестовый словарь.

# Легко тестировать, передавая любые данные
def generate_user_report(user_data: dict) -> str:
    # ...сложная логика формирования отчета
    return f"Report for {user_data['name']}"

Пример теста для хорошей функции

import unittest

def test_generate_user_report():
    mock_user = {"name": "Alice", "id": 1}
    report = generate_user_report(mock_user)
    assert "Report for Alice" in report

Такой подход позволяет в тестах подменять реальные зависимости (БД, API) на тестовые заглушки (моки), изолируя и проверяя только логику самой функции.