Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. AAA-паттерн: тесты, которые ловят баги
tests_that_catch_real_bugs

AAA-паттерн: тесты, которые ловят баги

Arrange-Act-Assert структура, понятные assert-сообщения, один тест — одна концепция

Вы уже умеете писать тесты. Но работающий тест и хороший тест — это разные вещи.

Цель: научиться писать тесты, которые легко читать, поддерживать и которые находят баги раньше, чем их увидят пользователи.

Проблема: плохие тесты

Реальный пример плохого теста:

def test_stuff(): u = User("Alice", 25) assert u.greet() == "Hello, I'm Alice!" u.celebrate_birthday() assert u.age == 26 u2 = User("Bob", 16) assert not u2.is_adult() # ... ещё 10 assert'ов

Что не так:

  • ❌ Непонятное название (test_stuff)
  • ❌ Проверяет сразу несколько вещей
  • ❌ Когда тест падает — непонятно ЧТО сломалось
  • ❌ Сложно модифицировать

Хороший тест должен:

  • ✅ Иметь понятное название
  • ✅ Проверять ОДНУ концепцию
  • ✅ Чётко показывать ЧТО сломалось
  • ✅ Легко читаться и поддерживаться

AAA Pattern: Arrange-Act-Assert

AAA — это структура теста из 3 частей:

  1. Arrange (подготовка) — создаём данные
  2. Act (действие) — вызываем тестируемый код
  3. Assert (проверка) — проверяем результат

Плохой тест: всё смешано

def test_bad(): user = User("Alice", 25) greeting = user.greet() assert greeting == "Hello, I'm Alice!" user.celebrate_birthday() assert user.age == 26

Проблема: непонятно где setup, где действие, где проверка.

Хороший тест: чёткая структура

def test_user_greeting(): # Arrange: подготовка данных user = User("Alice", 25) # Act: выполнение действия greeting = user.greet() # Assert: проверка результата assert greeting == "Hello, I'm Alice!"

Почему лучше:

  • ✅ Видно ЧТО тестируем (greet())
  • ✅ Видно КАК готовим данные (создаём пользователя)
  • ✅ Видно ЧТО проверяем (приветствие)

Ещё примеры AAA

def test_bank_account_deposit(): # Arrange account = BankAccount(initial_balance=100) # Act new_balance = account.deposit(50) # Assert assert new_balance == 150 assert account.balance == 150 def test_bank_account_insufficient_funds(): # Arrange account = BankAccount(initial_balance=50) # Act & Assert (для исключений можно объединить) with pytest.raises(ValueError) as exc_info: account.withdraw(100) assert "Insufficient funds" in str(exc_info.value)

Понятные assert messages

Проблема: непонятные ошибки

Плохой assert:

def test_discount(): price = calculate_discount(100, 0.1) assert price == 90

Когда тест падает:

AssertionError: assert 91 == 90

Непонятно: что за 91? Что за 90? Откуда взялась скидка 0.1?

Решение: assert с сообщением

def test_discount_with_message(): # Arrange original_price = 100 discount_rate = 0.1 expected_price = 90 # Act actual_price = calculate_discount(original_price, discount_rate) # Assert assert actual_price == expected_price, ( f"Discount calculation failed: " f"expected {expected_price} but got {actual_price} " f"for price {original_price} with {discount_rate * 100}% discount" )

Когда тест падает:

AssertionError: Discount calculation failed: expected 90 but got 91
for price 100 with 10.0% discount

✅ Теперь понятно ЧТО пошло не так!

One Concept Per Test

Проблема: тест проверяет слишком много

# ❌ ПЛОХО — тестирует И greeting, И birthday, И adult status def test_user_all_methods(): user = User("Alice", 17) assert user.greet() == "Hello, I'm Alice!" user.celebrate_birthday() assert user.age == 18 assert user.is_adult() == True

Когда тест падает на greet():

  • ❌ Остальные проверки НЕ запустятся
  • ❌ Непонятно, сломан только greet() или что-то ещё

Решение: отдельный тест для каждой концепции

# ✅ ХОРОШО — каждый тест проверяет ОДНУ вещь def test_user_greeting(): """Тест приветствия""" user = User("Alice", 17) greeting = user.greet() assert greeting == "Hello, I'm Alice!" def test_user_celebrates_birthday(): """Тест увеличения возраста""" user = User("Alice", 17) new_age = user.celebrate_birthday() assert new_age == 18 assert user.age == 18 def test_user_becomes_adult_after_birthday(): """Тест: после 18-го дня рождения становится взрослым""" user = User("Alice", 17) user.celebrate_birthday() # Теперь 18 assert user.is_adult() == True, ( f"User should be adult after turning {user.age}" )

Преимущества:

  • ✅ Если greet() сломан — другие тесты всё равно запустятся
  • ✅ По названию теста понятно ЧТО сломалось
  • ✅ Легко добавить новые тесты

Именование тестов: описывайте поведение

Плохие названия

# ❌ ПЛОХО def test_1(): pass def test_user(): pass def test_function(): pass

Проблема: не понятно ЧТО тестируется.

Хорошие названия

# ✅ ХОРОШО — описывает ПОВЕДЕНИЕ def test_user_greets_with_their_name(): """Приветствие содержит имя пользователя""" pass def test_deposit_increases_balance(): """Пополнение увеличивает баланс""" pass def test_withdraw_fails_when_insufficient_funds(): """Снятие выбрасывает ошибку при недостатке средств""" pass

Formula для названий

Pattern: test_[что]_[делает]_[при_каких_условиях]

Примеры:

  • test_discount_applies_correctly_for_vip_users
  • test_login_fails_with_wrong_password
  • test_email_sends_when_order_confirmed

Практический пример: до и после

До: плохой тест

def test_shopping_cart(): cart = ShoppingCart() cart.add_item("apple", 1.50) cart.add_item("bread", 2.00) assert cart.total() == 3.50 cart.apply_discount(0.1) assert cart.total() == 3.15 cart.remove_item("apple") assert cart.total() == 1.80

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

def test_empty_cart_has_zero_total(): """Пустая корзина имеет итог 0""" # Arrange cart = ShoppingCart() # Act total = cart.total() # Assert assert total == 0, "Empty cart should have total of 0" def test_adding_items_increases_total(): """Добавление товаров увеличивает итог""" # Arrange cart = ShoppingCart() # Act cart.add_item("apple", price=1.50) cart.add_item("bread", price=2.00) total = cart.total() # Assert assert total == 3.50, ( f"Expected total 3.50 for apple(1.50) + bread(2.00), got {total}" ) def test_discount_reduces_total_by_percentage(): """Скидка уменьшает итог на процент""" # Arrange cart = ShoppingCart() cart.add_item("apple", price=1.50) cart.add_item("bread", price=2.00) discount = 0.1 # 10% expected_total = 3.15 # Act cart.apply_discount(discount) actual_total = cart.total() # Assert assert actual_total == expected_total, ( f"10% discount on 3.50 should give {expected_total}, " f"but got {actual_total}" )

Что вы изучили

  • AAA Pattern — структура Arrange-Act-Assert
  • Понятные assert messages — сообщения для отладки
  • One concept per test — один тест = одна проверка
  • Хорошие названия тестов — описание поведения
  • Тесты как документация — код объясняет сам себя

Устранение неисправностей

Проверьте свои знания

Вопросы ещё не добавлены

Вопросы для этой подтемы ещё не добавлены.

Далее: Parametrize: один тест — десятки сценариев