ATDD переворачивает обычный порядок: QA пишет Playwright acceptance-тест до того как фича существует, тест падает, и фича считается готовой когда тест проходит. Этот единственный сдвиг переводит QA из режима верификации готовой работы в режим определения что означает "готово". Статья разбирает цикл Red-Green-Refactor, как формат BDD Given/When/Then вписывается в рабочий процесс трёх участников, и что меняется в ежедневной работе QA когда разработчики уже практикуют TDD.

Цикл TDD: Red, Green, Refactor

TDD следует строгому трёхшаговому циклу. Red: написать тест который падает (потому что фича ещё не существует). Green: написать минимальный код чтобы тест прошёл. Refactor: почистить код сохраняя тесты зелёными. Цикл повторяется для каждого небольшого куска функциональности.

// Фаза Red: пишем падающий тест
test('calculateDiscount applies 10% for code SAVE10', () => {
  const result = calculateDiscount(100, 'SAVE10');
  expect(result).toBe(90);
});

// Запуск теста падает: calculateDiscount не определена

// Фаза Green: пишем минимальный код для прохождения
function calculateDiscount(price: number, code: string): number {
  if (code === 'SAVE10') {
    return price * 0.9;
  }
  return price;
}

// Теперь тест проходит

// Фаза Refactor: чистим код не ломая тест
const DISCOUNT_CODES: Record<string, number> = {
  SAVE10: 0.1,
  SAVE20: 0.2,
  SAVE50: 0.5,
};

function calculateDiscount(price: number, code: string): number {
  const discount = DISCOUNT_CODES[code] || 0;
  return price * (1 - discount);
}

// Тесты по-прежнему проходят: рефакторинг ничего не сломал

Преимущества TDD влияющие на QA

Естественно тестируемый код. Код написанный через TDD как правило имеет небольшие функции с чёткими входными и выходными данными: проще тестировать на каждом уровне. Защита от регрессий. Каждое поведение защищено тестом. Когда QA находит баг, разработчики могут воспроизвести его тестом, исправить, и тест предотвращает регрессию. Документация через тесты. TDD-тесты описывают что должен делать код в конкретных сценариях. QA может читать тесты чтобы понять ожидаемое поведение. Раннее обнаружение багов. Проблемы пойманные при написании юнит-тестов стоят на порядки дешевле в исправлении чем проблемы найденные в QA или продакшне.

Behaviour-Driven Development (BDD)

BDD расширяет TDD в направлении бизнес-уровня. Где TDD фокусируется на том как работает код, BDD фокусируется на том какое поведение требуется.

Три участника: разработчик, QA и продукт-менеджер, совместно определяют поведение в формате Given/When/Then до написания любого кода.

Feature: Discount codes

  Scenario: Valid discount code reduces price
    Given I have a cart with items totaling $100
    When I apply discount code "SAVE10"
    Then the total should be $90
    And the discount amount should be $10

  Scenario: Invalid discount code shows error
    Given I have a cart with items totaling $100  
    When I apply discount code "INVALID"
    Then I should see "Invalid discount code"
    And the total should remain $100

  Scenario: Expired discount code shows error
    Given I have a cart with items totaling $100
    When I apply discount code "EXPIRED20"
    Then I should see "This discount code has expired"

Как QA может применять мышление TDD

Даже не пиша юнит-тесты, можно применять мышление TDD.

Пиши тест-кейсы до начала ручного тестирования

Вместо исследовательского тестирования с нуля: сначала записывай ожидаемые поведения.

Feature: Password Reset

Что я ожидаю:
1. Ввод валидного email отправляет письмо сброса в течение 2 минут
2. Ввод невалидного email показывает "Email не найден"
3. Ссылка сброса истекает через 24 часа
4. Использование истёкшей ссылки показывает "Ссылка устарела" с опцией запросить новую
5. После успешного сброса старый пароль больше не работает
6. Ссылку сброса можно использовать только один раз

Тест-план готов до начала тестирования.

Acceptance Test-Driven Development (ATDD)

Сотрудничай с разработчиками чтобы писать acceptance-тесты до того как фича построена:

// Написан до того как фича существует
test('checkout with valid card completes purchase', async ({ page }) => {
  // Подготовка: пользователь с товарами в корзине
  // ...

  // Выполнить чекаут
  await page.fill('[data-testid="card-number"]', '4242 4242 4242 4242');
  await page.fill('[data-testid="card-expiry"]', '12/28');
  await page.fill('[data-testid="card-cvc"]', '123');
  await page.click('[data-testid="pay-now"]');
  
  // Ожидаемые результаты
  await expect(page).toHaveURL('/order-confirmation');
  await expect(page.getByTestId('order-number')).toBeVisible();
  await expect(page.getByTestId('success-message')).toContainText('Payment successful');
});

Эти тесты падают пока фича не построена. Когда проходят: фича готова.

Когда разработчики практикуют TDD: что QA должен знать

Что меняется для QA

Покрытие юнит-тестами уже существует. Разработчики уже протестировали edge case на уровне кода. QA может фокусироваться на точках интеграции, пользовательских флоу и бизнес-логике, а не базовом поведении функций. Сбои тестов: информативные. Когда CI TDD-команды ломается: конкретно. "Функция X возвращает неверное значение для edge case Y", а не "приложение где-то сломано". Рефакторинг безопаснее. При всестороннем покрытии юнит-тестами QA не нужно перетестировать базовую функциональность после рефакторинга: тесты уже покрывают её.

Что QA по-прежнему должен делать

Интеграционное тестирование. Юнит-тесты тестируют функции в изоляции. QA тестирует как компоненты работают вместе: база данных, API, фронтенд, система email. Тестирование с пользовательской перспективы. Разработчики тестируют "работает ли код?". QA тестирует "понятна ли фича в использовании?". Обнаружение edge case. Исследовательское тестирование QA находит сценарии которые не были специфицированы. TDD покрывает только то о чём думали при написании тестов. Производительность и надёжность. TDD не покрывает время ответа, одновременных пользователей или поведение под нагрузкой.

Практический TDD для тест-автоматизации

При построении тест-фреймворка применяй принципы TDD:

// Вместо написания полного хелпера сначала: пишем тест для желаемого поведения
test('login helper should redirect to dashboard after login', async ({ page }) => {
  await loginAs(page, 'member');  // Функция ещё не существует
  
  await expect(page).toHaveURL('/dashboard');
});

// Теперь пишем минимальную реализацию
async function loginAs(page: Page, role: 'admin' | 'member') {
  const credentials = {
    admin: { email: 'admin@test.com', password: 'AdminPass1' },
    member: { email: 'member@test.com', password: 'MemberPass1' },
  };
  
  await page.goto('/login');
  await page.fill('[data-testid="email"]', credentials[role].email);
  await page.fill('[data-testid="password"]', credentials[role].password);
  await page.click('[data-testid="submit"]');
  await page.waitForURL('/dashboard');
}

Это предотвращает оверинжиниринг. Строишь ровно то что нужно, и тест подтверждает что работает.

Пирамида тестирования в контексте TDD

TDD напрямую формирует пирамиду тестирования:

         /\
        /E2E\          <- QA автоматизация (мало, медленно)
       /------\
      / Integr.\       <- Интеграционные тесты (умеренно)
     /----------\
    /  Unit Tests \    <- Вывод TDD (много, быстро)
   /--------------\

TDD производит широкое основание юнит-тестов. QA строит интеграционный и E2E-слои сверху. Вместе они образуют пирамиду где большинство багов ловится дёшево на уровне юнит-тестов, с небольшим сфокусированным набором дорогих E2E-тестов.

Итог

  • Цикл TDD: Red (падающий тест) → Green (сделать проходящим) → Refactor (почистить)
  • TDD производит естественно тестируемый, хорошо структурированный код
  • BDD расширяет TDD к бизнес-поведению через сценарии Given/When/Then
  • ATDD: QA пишет acceptance-тесты до начала разработки: тесты падают пока фича не построена
  • Когда разработчики практикуют TDD: QA сдвигает фокус на интеграцию, пользовательские флоу и edge case
  • Применяй мышление TDD к тест-автоматизации: сначала пиши интерфейс теста, потом реализуй

TDD и QA дополняют друг друга. TDD ловит баги уровня юнит-тестов рано. QA ловит проблемы интеграции и пользовательского опыта. Вместе они производят ПО которое работает корректно на каждом уровне.

→ See also: Разработка через тестирование: руководство QA-инженера | Shift-left тестирование: что это значит и как практиковать | Пирамида тестирования: объяснение для QA-инженеров