Зачем вообще нужны тесты

Тесты — это не просто способ сделать CI «зелёным».
Это один из фундаментальных слоёв любого проекта.

Хорошие тесты:

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

Плохие тесты:

  • замедляют разработку
  • ломаются без причины
  • не дают уверенности в коде
  • мешают рефакторингу

Принципы FIRST

При написании тестов придерживайтесь принципов FIRST:

Fast (быстрые)

Тесты должны выполняться быстро.

  • избегайте реального I/O (сеть, файловая система, БД)
  • не используйте sleep
  • минимизируйте тяжёлые setup’ы

Independent (независимые)

Тесты не должны зависеть друг от друга.

  • порядок запуска не должен иметь значения
  • никакого shared state
  • каждый тест подготавливает своё окружение

Repeatable (повторяемые)

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

  • не зависят от времени (datetime.now())
  • не зависят от случайности (random)
  • не зависят от внешних сервисов

Self-validating (самопроверяемые)

Результат теста должен быть однозначным.

  • никаких ручных проверок
  • чёткие assert
  • понятные сообщения об ошибках

Timely (своевременные)

Тесты должны быть простыми в поддержке.

  • пишутся рядом с кодом
  • легко читаются
  • легко изменяются при изменении логики

Структура теста (AAA)

Каждый тест должен иметь чёткую структуру:

  • Arrange — подготовка данных  
  • Act — выполнение действия  
  • Assert — проверка результата

Пример:

def test_user_can_send_message():
    # Arrange
    user = User()
    chat = Chat()
 
    # Act
    chat.send_message(user, "hello")
 
    # Assert
    assert chat.messages_count == 1

Один тест — одна ответственность

Тест должен проверять одну логическую вещь.

❌ Плохо:

def test_chat():
	#...
    assert response.messages
    
    message_added = response.messages[0]
    assert message_added.text == "hello"
    assert message_added.author == user

✅ Хорошо:

def test_messages_return():
	#...
    assert response.messages
 
def test_add_message():
	#...
    message_added = response.messages[0]
    assert message_added.text == "hello"
    assert message_added.author == user

Разделяйте тесты по сценариям

Не делайте «простыни» на 1000 строк.

❌ Плохо:

  • один файл
  • все сценарии вперемешку
  • сложно ориентироваться

✅ Хорошо:

  • группировка по контекстам / модулям
  • отдельные файлы/классы для разных сценариев
  • понятная структура

Пример:

api/customers/events/tests/
    test_get.py
    test_edit.py
    test_filters.py
    test_meta.py

Минимизируйте setup

Тест должен быть максимально простым.

❌ Плохо:

  • сложные фабрики
  • много неиспользуемых данных
  • «магия» в фикстурах

✅ Хорошо:

  • только необходимые данные
  • явный setup
  • читаемость важнее «умности»

Избегайте реальных зависимостей

Не используйте в тестах:

  • реальные БД
  • сеть
  • файловую систему (если можно избежать)

Используйте:

  • mock / stub / fake
  • in-memory реализации

Избегайте нестабильных (flaky) тестов

Проблемные признаки:

  • тест «иногда падает»
  • зависит от времени
  • зависит от порядка запуска

Решение:

  • фиксируйте время
  • убирайте randomness
  • изолируйте состояние

Именование тестов

Имя теста должно объяснять поведение, а не реализацию.

❌ Плохо:

def test_1():
def test_chat():

✅ Хорошо:

def test_user_can_send_message():
def test_message_is_not_sent_without_permission():

Рефакторинг тестов

Тесты — это тоже код. Их нужно поддерживать.

При работе над фичей:

  • увидели сложный тест → упростите
  • тест проверяет несколько вещей → разделите
  • есть дублирование → уберите
  • файл слишком большой → разбейте

Не откладывайте — делайте это сразу.

Антипаттерны (“запах” плохих тестов)

  • ❌ Огромные тесты на сотни строк
  • ❌ Много логики внутри теста
  • ❌ Один тест проверяет всё
  • ❌ Нестабильные тесты
  • ❌ Тесты, которые сложно читать

Итог

Хорошие тесты:

  • быстрые
  • простые
  • изолированные
  • читаемые
  • отражают бизнес-логику

Главная цель:

Тесты должны помогать понимать систему и безопасно её менять, а не мешать разработке.