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

При написании unit-тестов, ты ориентируешься на тест-кейс или сам выбираешь структуру

2.0 Middle🔥 231 комментариев
#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Выбор структуры unit-тестов: гибкость и практичность

Это отличный вопрос, потому что показывает, насколько ты гибкий в своём подходе и понимаешь разницу между идеалом и практикой.

Коротко: оба подхода имеют смысл

Если есть явно определённые тест-кейсы (требования, чек-листы, баги) — я их использую. Если их нет — выбираю структуру сам, основываясь на опыте и best practices.

Сценарий 1: Есть чёткие тест-кейсы

Если компания дала тебе документ с требованиями к тестированию:

Тест-кейс 1: Проверить, что функция принимает email и возвращает True если валиден
Тест-кейс 2: Проверить, что функция возвращает False если email содержит 2 символа @
Тест-кейс 3: Проверить, что функция игнорирует пробелы
...

Тогда я должен следовать этим требованиям:

import pytest
from validators import validate_email

class TestEmailValidation:
    """Тест-кейсы для валидации email (по требованиям)"""
    
    def test_valid_email_returns_true(self):
        """Тест-кейс 1: валидный email"""
        assert validate_email("user@example.com") is True
    
    def test_invalid_email_with_double_at_returns_false(self):
        """Тест-кейс 2: двойной @ возвращает False"""
        assert validate_email("user@@example.com") is False
    
    def test_ignores_leading_trailing_spaces(self):
        """Тест-кейс 3: игнорирует пробелы"""
        assert validate_email(" user@example.com ") is True

Преимущества: требования полностью покрыты, легко отследить, почему падает тест.

Сценарий 2: Нет чётких тест-кейсов

Если требования не документированы, я сам выбираю подход. Обычно использую:

AAA Pattern (Arrange-Act-Assert):

def test_calculate_discount_applies_percentage():
    # Arrange — подготовка данных
    product_price = 100
    discount_percent = 20
    
    # Act — выполнение функции
    result = calculate_discount(product_price, discount_percent)
    
    # Assert — проверка результата
    assert result == 80

Плюсы:

  • Очень читаемо
  • Легко понять, что происходит
  • Можно быстро отладить

Сценарий 3: TDD (Test-Driven Development)

Если работаю по TDD (сначала тест, потом код), структура немного отличается:

  1. Пишу падающий тест на базе требований
  2. Пишу минимальный код для прохождения
  3. Рефакторю
# ШАГ 1: Падающий тест (RED)
def test_user_can_reset_password_with_valid_token():
    user = User(email="test@example.com")
    token = user.generate_reset_token()
    
    new_password = "NewSecurePassword123"
    result = user.reset_password(token, new_password)
    
    assert result is True
    assert user.check_password(new_password) is True

# ШАГ 2: Минимальный код (GREEN)
class User:
    def reset_password(self, token, new_password):
        if self.validate_reset_token(token):
            self.password = hash_password(new_password)
            return True
        return False

# ШАГ 3: Рефакторинг (REFACTOR)
# Оптимизация, улучшение читаемости, убрать дублирование

Практическая матрица выбора

СитуацияПодходСтруктура
Чёткие требованияСледовать требованиямПо тест-кейсам
Нет требованийTDDСначала тест
Легаси кодНет тестов, добавляюAAA паттерн
Баг реализмТест, потом фиксRed-Green-Refactor
Интеграционные тестыСценарии пользователяBDD-style

Пример: как я подхожу на практике

Возьму задачу: "Написать функцию, которая находит наибольший общий делитель".

Нет требований — буду сам выбирать:

import pytest
from math import gcd

class TestGCD:
    """Тесты для функции нахождения НОД"""
    
    # Группирую по сценариям
    class TestBasicCases:
        def test_gcd_of_two_positive_numbers(self):
            assert gcd(48, 18) == 6
        
        def test_gcd_when_one_divides_other(self):
            assert gcd(10, 5) == 5
        
        def test_gcd_of_coprime_numbers(self):
            assert gcd(17, 19) == 1
    
    class TestEdgeCases:
        def test_gcd_with_zero(self):
            assert gcd(0, 5) == 5
        
        def test_gcd_with_negative_numbers(self):
            assert gcd(-48, 18) == 6
    
    class TestErrorHandling:
        def test_gcd_with_non_integers(self):
            with pytest.raises(TypeError):
                gcd("5", 10)

Структура понятна:

  • Базовые случаи
  • Edge cases
  • Обработка ошибок

Важные принципы при выборе

1. Читаемость

Тест должен быть похож на документацию:

# Плохо: непонятно, что тестируем
def test_1():
    assert func(5, 3) == 2

# Хорошо: понятно сразу
def test_subtraction_returns_difference():
    assert subtract(5, 3) == 2

2. Независимость

Каждый тест должен работать отдельно:

# Плохо: тесты зависят друг от друга
def test_create_user():
    global user
    user = User(name="John")
    assert user is not None

def test_get_user_name():
    assert user.name == "John"  # зависит от test_create_user

# Хорошо: каждый тест независим
def test_create_user():
    user = User(name="John")
    assert user is not None

def test_get_user_name():
    user = User(name="John")
    assert user.name == "John"

3. Скорость

Unit-тесты должны быть быстрыми:

# Плохо: делаем HTTP запрос в unit-тесте
def test_get_user_by_id():
    response = requests.get("https://api.example.com/users/1")  # Медленно!
    assert response.status_code == 200

# Хорошо: мокируем внешние зависимости
from unittest.mock import patch

def test_get_user_by_id():
    with patch("requests.get") as mock_get:
        mock_get.return_value.status_code = 200
        response = requests.get("https://api.example.com/users/1")
        assert response.status_code == 200

Когда я спрашиваю уточнения

На интервью я бы уточнил:

  1. "Есть ли определённые требования к структуре тестов?"
  2. "Какой фреймворк вы используете (pytest, unittest)?"
  3. "Используете ли вы TDD?"
  4. "Какой уровень покрытия требуется?"
  5. "Есть ли существующие тесты, которые я должен следовать?"

Итог

Я гибкий разработчик:

  • Если есть чёткие требования → слежу за ними
  • Если требований нет → использую TDD и best practices
  • Приоритет: читаемость → скорость → покрытие
  • Всегда готов адаптироваться к стилю команды

Основной принцип: тест — это документация, которая выполняется. Поэтому она должна быть ясной и поддерживаемой.