В TDD-команде разработчики пишут тесты до кода, что поднимает практический вопрос: что остаётся QA? Юнит-тесты не заменяют интеграционные тесты, E2E-верификацию или edge case которые разработчики не догадались специфицировать. Статья разбирает как QA вписывается в TDD-рабочие процессы, что такое acceptance test-driven development (ATDD) на практике, и почему TDD требует сдвига QA-работы раньше, а не её устранения.

Что такое TDD на самом деле

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

Ключевое слово: "минимальный". Пишется ровно столько кода чтобы тест прошёл, и ни строчки больше. Это заставляет реализацию следовать из спецификации (теста), а не наоборот.

Что TDD меняет в коде

TDD производит код который:

Тестируемый по дизайну. Код который сложно тестировать часто сигнализирует о плохом дизайне: тесная связность, скрытые зависимости, слишком много ответственностей. TDD принуждает к тестируемости в момент написания, а не как запоздалую мысль. Покрытый по определению. У каждого поведения есть тест, потому что нужно было написать тест чтобы написать поведение. Инкрементальный. Фичи строятся небольшими верифицированными шагами. Нет "написать всё, потом тестировать". Задокументированный. Тест-сьют: исполняемая спецификация. Чтение тестов говорит что должен делать код.

Место QA-инженеров в TDD-командах

QA-инженеры в TDD-командах сталкиваются с парадоксом: разработчики уже пишут тесты. Что остаётся?

Несколько вещей.

Тестирование более высокого уровня. Разработчики пишут юнит-тесты. QA-инженеры пишут интеграционные и E2E-тесты. TDD на уровне юнит-тестов не заменяет E2E-верификацию. Acceptance test-driven development (ATDD). QA-инженеры пишут acceptance-тесты до того как разработчики пишут любой код. Разработчики делают эти тесты проходящими. Это TDD на уровне фичи. Требует участия QA с начала разработки, а не с конца. Исследовательское тестирование и edge case. TDD-тесты пишут разработчики которые знают как работает система. QA-инженеры тестируют то что разработчики не догадались специфицировать: edge case, необычные входные данные, точки интеграции, производительность под нагрузкой. Ревью качества тестов. Разработческие тесты реально тестируют правильные вещи? Они тестируют поведение или реализацию? Поймают ли они важные баги? QA-инженеры могут ревьюить тест-код, а не только продукт-код.

Acceptance test-driven development (ATDD)

ATDD: QA-релевантная версия TDD. Рабочий процесс:

1. Бизнес-стейкхолдер описывает фичу в пользовательских терминах

2. QA-инженер переводит это в acceptance criteria и acceptance-тесты

3. Разработчики реализуют до тех пор пока acceptance-тесты не проходят

4. QA верифицирует и тесты и реализацию

Acceptance-тесты становятся источником истины о том что означает "готово". Фича готова когда её acceptance-тесты проходят.

В терминах Playwright:

// Написан до того как реализация существует
test('user can reset password via email', async ({ page }) => {
  // Дано: пользователь с зарегистрированным email
  const userEmail = 'testuser@example.com';
  
  // Когда: запрашивает сброс пароля
  await page.goto('/forgot-password');
  await page.getByLabel('Email address').fill(userEmail);
  await page.getByRole('button', { name: 'Send reset link' }).click();
  
  // Тогда: видит сообщение подтверждения
  await expect(page.getByRole('alert')).toHaveText(
    'If that email is registered, a reset link is on its way.'
  );
  
  // И: получает письмо сброса (верифицируется через test email API)
  const inbox = await testEmailAPI.getInbox(userEmail);
  expect(inbox.some(m => m.subject.includes('Reset your password'))).toBe(true);
});

Этот тест написан до того как фича построена. Задача разработчика: сделать его проходящим.

Behaviour-Driven Development (BDD)

BDD: эволюция ATDD которая добавляет общий язык (обычно Gherkin) для написания тестов которые могут читать не-инженеры:

Feature: Password reset

  Scenario: User resets password via email
    Given a user with email "testuser@example.com" is registered
    When they submit the forgot password form with that email
    Then they see "If that email is registered, a reset link is on its way."
    And they receive a password reset email within 1 minute

Эти Gherkin-сценарии маппятся на Playwright-тест-код через слой определений шагов (с использованием инструментов вроде Cucumber.js или BDD-плагинов Playwright).

BDD наиболее ценен когда нетехнические стейкхолдеры пишут или ревьюят сценарии. Он добавляет накладные расходы для команд где все стейкхолдеры могут читать код.

Должны ли QA-инженеры практиковать TDD сами?

Для кода тест-автоматизации: в основном нет. TDD предназначен для продакшн-кода где проектируется система. Тест-код должен быть простым в написании и верификации. Написание тестов для тест-кода создаёт излишнюю сложность.

Исключение: QA-инженеры которые строят тест-фреймворки или утилиты (хелперы page object, утилиты фикстур, интеграции с отчётностью) могут выиграть от TDD при создании этих инструментов.

Практический вывод для QA в TDD-командах

1. Включайся до написания кода. Ревьюй user stories на тестируемость. Пиши acceptance criteria. Определяй edge case.

2. Пиши acceptance-тесты рано. Даже если разработчики не практикуют формальный ATDD, написание acceptance-тестов до начала разработки формирует реализацию.

3. Сдвигай исследовательское тестирование раньше. В TDD-командах исследовательское тестирование важнее, потому что именно оно находит то что тесты не специфицировали.

4. Ревьюй разработческие тесты. Не по числам покрытия, а по тому действительно ли тесты верифицируют поведение которое имеет значение.

TDD не устраняет потребность в QA-инженерах. Он меняет где происходит QA-работа: не после кода, а вместе с ним.

→ See also: Test-Driven Development для QA: написание тестов до кода | Shift-left тестирование: что это значит и как практиковать | Практики автоматизации тестирования, которые реально важны