Паттерн AAA: Arrange-Act-Assert

Простой скелет, который делает любой тест читаемым: подготовил — сделал — проверил.

AAA (Arrange-Act-Assert) — структура теста из трёх частей: подготовка данных (Arrange), выполнение проверяемого действия (Act) и проверка результата (Assert).

Три части любого хорошего теста

  • Arrange. Готовим всё необходимое: объекты, входные данные, состояние.
  • Act. Выполняем ровно одно проверяемое действие.
  • Assert. Сравниваем результат с ожиданием.

Разделение на эти три блока делает тест читаемым: с первого взгляда видно, что готовится, что вызывается и что проверяется. Это давно стало негласным стандартом.

def apply_coupon(price, coupon):
    """Применяет купон к цене."""
    coupons = {"SAVE10": 0.10, "HALF": 0.50}
    rate = coupons.get(coupon, 0)
    return round(price * (1 - rate), 2)


def test_apply_coupon():
    # Arrange — готовим данные
    price = 200
    coupon = "HALF"

    # Act — выполняем одно действие
    result = apply_coupon(price, coupon)

    # Assert — проверяем результат
    assert result == 100.0

test_apply_coupon()
print("Тест в стиле AAA пройден: купон HALF даёт 100.0")

Вывод:

Тест в стиле AAA пройден: купон HALF даёт 100.0

Один тест — одна мысль

Из AAA вытекает важное правило: один тест проверяет одно поведение. Если хочется проверить и купон HALF, и неизвестный купон, и нулевую цену — сделайте три отдельных теста. Тогда при падении сразу ясно, что именно сломалось, а не «где-то в большом тесте».

def apply_coupon(price, coupon):
    coupons = {"SAVE10": 0.10, "HALF": 0.50}
    return round(price * (1 - coupons.get(coupon, 0)), 2)

# Три отдельных теста — каждый про одно
def test_known_coupon():
    assert apply_coupon(100, "SAVE10") == 90.0

def test_unknown_coupon():
    assert apply_coupon(100, "NOPE") == 100.0   # неизвестный -> без скидки

def test_half():
    assert apply_coupon(50, "HALF") == 25.0

for t in (test_known_coupon, test_unknown_coupon, test_half):
    t()
print("3 отдельных теста по одной проверке — упадёт ровно тот, что сломан")

Вывод:

3 отдельных теста по одной проверке — упадёт ровно тот, что сломан

Хорошее имя теста

Имя теста — часть документации. test_unknown_coupon_keeps_full_price читается как утверждение о поведении. Хорошее имя описывает сценарий и ожидание, а не реализацию.

Итог

  • AAA: Arrange (подготовка) → Act (действие) → Assert (проверка).
  • Один тест проверяет одно поведение — так понятно, что именно сломалось.
  • Имя теста — это утверждение о поведении, а не описание кода.
Проверьте себя
1. Что означает паттерн AAA?
AAdd-Average-Aggregate
BArrange (подготовка) — Act (действие) — Assert (проверка)
CAsync-Await-Abort
DAnalyze-Adapt-Apply
2. Почему один тест должен проверять одно поведение?
AЧтобы тестов было больше для отчёта
BЧтобы при падении сразу было ясно, что именно сломалось
CЧтобы тесты дольше выполнялись
DТак требует Python
3. Каким должно быть хорошее имя теста?
AСлучайным набором букв
BУтверждением о сценарии и ожидаемом поведении
CОписанием внутренней реализации
DПросто test1, test2, test3
Поддержать проект