Тестирование ПО Ручное тестирование: исследовательское, смоук, регресс, приёмочное
0%

Ручное тестирование: исследовательское, смоук, регресс, приёмочное

Ручное тестирование: исследовательское, смоук, регресс, приёмочное

Есть удобный миф: ручное тестирование — временная стадия, которую индустрия проходит по пути к полной автоматизации. Миф удобен всем. Менеджеру он обещает, что затраты на QA когда-нибудь станут нулевыми. Разработчику — что «ручники» просто не умеют писать код. Самому тестировщику — повод тревожиться и переучиваться на автоматизатора.

Проблема мифа в том, что он путает две разные вещи: повторение известных проверок и поиск неизвестного. Первое действительно нужно автоматизировать — компьютер повторяет лучше человека, дешевле и без скуки. Второе автоматизировать невозможно в принципе, потому что автоматизировать можно только то, что вы уже придумали. Скрипт проверяет ответ на вопрос, который вы задали позавчера. Человек задаёт новый вопрос прямо сейчас, глядя на экран.

Эта статья — про вторую часть работы и про четыре её основные формы. Мы уже разобрали, как выбирать проверки (https://courses.digitable.life/post/testing/02-test-design/) и как их оформлять (https://courses.digitable.life/post/testing/03-documentation/). Здесь — как эти проверки реально выполняются руками, в каком режиме, с каким бюджетом времени и по каким критериям останавливаться.

Четыре вида ручного тестирования в координатах ширины и глубины

1. Проблема оракула: почему человека нельзя выкинуть

В теории тестирования оракул — это способ узнать, правильно ли повёл себя продукт. Автотест содержит оракул внутри себя в виде assert: ожидаемое значение зафиксировано заранее. И это его фундаментальное ограничение — автотест умеет проверить только то свойство, которое кто-то догадался записать.

Посмотрим на конкретный экран. Автотест проверяет:

await expect(page.getByTestId('order-total')).toHaveText('12 480 ₽');

Тест зелёный. Тем временем на экране:

  • сумма отображается корректно, но кнопка «Оплатить» уехала за пределы вьюпорта на 13-дюймовом ноутбуке;
  • рядом висит прошлогодний баннер «Скидка до 31 декабря»;
  • в позиции товара написано «1 шт.» при количестве 3 — в сумме учтено верно, в строке нет;
  • страница отрисовалась за 9 секунд, и все 9 секунд был белый экран без индикатора;
  • в письме подтверждения имя клиента — «undefined».

Ни одна из этих проблем не «падает» ни в одном автотесте, потому что ни одна из них не была заранее сформулирована как утверждение. Человек замечает их за десять секунд — не потому что он внимательнее машины, а потому что у него принципиально другой оракул: набор нечётких ожиданий о том, каким продукт должен быть.

Джеймс Бах и Майкл Болтон свели эти ожидания в мнемонику HICCUPPS(F) — список источников, по которым человек判 судит о неправильности (The Oracle Problem and the Teaching of Software Testing):

Буква Источник ожидания Вопрос, который вы задаёте
H History Раньше работало иначе. Это осознанное изменение?
I Image Так может выглядеть продукт компании с такой репутацией?
C Comparable products У конкурента/соседнего модуля это сделано иначе. Почему?
C Claims Мы сами обещали это в доке, релиз-ноутах, рекламе
U User expectations Разумный пользователь ожидал бы другого
P Product Внутри самого продукта два места ведут себя противоречиво
P Purpose Формально по спеке верно, но задачу пользователя не решает
S Standards Нарушены стандарты, законы, гайдлайны платформы, WCAG
F Familiarity Похоже на класс дефектов, который у нас уже случался

Практический приём: когда «что-то не так, но по спеке всё верно» — прогоните ощущение по этому списку. Он превращает «мне не нравится» в формулировку, которую можно защитить перед разработчиком и продактом. Баг-репорт «неудобно» закроют как won't fix; баг-репорт «противоречит нашему же тексту в справке (Claims) и гайдлайну iOS (Standards)» — почините.

Разработчику. Это и есть ответ на вопрос «зачем нам тестировщик, если у меня зелёный CI». Ваш CI проверяет соответствие коду ваших же ожиданий. Тестировщик проверяет соответствие ожиданиям, которых у вас не было. Это не дублирование — это другой источник информации.

2. Карта видов: что чем занимается

Ключевое различие, которое стоит держать в голове: смоук и регресс отвечают на закрытые вопросы («работает ли то, что мы уже описали?»), исследовательское — на открытый («что здесь вообще происходит?»), приёмочное — на вопрос о деньгах («мы это принимаем?»). Разные вопросы требуют разного режима работы, разной документации и разных метрик успеха. Смешивать их — самая частая организационная ошибка: команда «делает регресс», в котором человек попутно исследует, отвлекается, не проходит половину чек-листа и не может внятно отчитаться ни по одному фронту.

3. Смоук-тестирование: контракт «билд пригоден для работы»

Название пришло из электроники: собрал плату, подал питание, если дым не пошёл — можно продолжать проверки. Смысл тот же — смоук не ищет баги, он защищает время команды. Если билд разваливается на логине, нет смысла тратить восемь человеко-часов на регресс.

Три свойства правильного смоука:

  1. Короткий. 10–15 минут максимум, иначе он не будет выполняться каждый раз.
  2. Широкий и мелкий. По одному самому простому happy-path сценарию на каждую критичную подсистему. Никаких граничных значений — они в регрессе.
  3. Бинарный и блокирующий. Результат — «билд принят» / «билд отклонён». Есть фейл — билд возвращается разработчику, дальнейшее тестирование не начинается. Смоук без права заблокировать — ритуал, а не гейт.

Пример чек-листа для e-commerce, который влезает в 12 минут:

## Смоук: релиз-кандидат RC-2.14, окружение staging

Предусловия: свежий деплой, миграции применены, версия в /health совпадает с ожидаемой.

- [ ] `/health` отвечает 200, поле `version` = RC-2.14, `db: ok`, `redis: ok`
- [ ] Главная страница открывается, есть товары, нет 5xx в консоли браузера
- [ ] Логин существующим пользователем (smoke_user@example.com) — успешен
- [ ] Поиск по слову «кофе» возвращает непустой результат
- [ ] Карточка товара открывается, цена и наличие отображаются
- [ ] Товар добавляется в корзину, счётчик обновился
- [ ] Оформление заказа доходит до страницы оплаты (тестовая карта 4111...)
- [ ] Заказ появился в личном кабинете и в админке
- [ ] Письмо-подтверждение пришло в mailhog
- [ ] Логи приложения без ERROR за время прохождения

Вердикт: ПРИНЯТ / ОТКЛОНЁН (причина, ссылка на баг)
Время: ____ мин. Исполнитель: ____

Обратите внимание: в чек-листе нет шагов «нажмите кнопку X». Это осознанно — чек-лист пишется для того, кто знает продукт, и проверяет результаты, а не диктует действия. Разница между чек-листом и тест-кейсом разобрана в https://courses.digitable.life/post/testing/03-documentation/; для смоука почти всегда правильный формат — именно чек-лист.

Смоук против санити

Термины путают постоянно, и в разных компаниях они означают разное. Рабочее разделение:

Смоук Санити (sanity)
Когда На каждый билд/деплой После точечного фикса или мелкого изменения
Охват Широкий, все критичные подсистемы Узкий, только затронутая область + соседи
Глубина Минимальная Достаточная, чтобы поверить в фикс
Формализация Фиксированный чек-лист Обычно ad-hoc, по ситуации
Вопрос Билд вообще пригоден? Конкретно эта правка вменяема?

Если в вашей команде эти слова используются как синонимы — не воюйте с терминологией, договоритесь о смысле внутри команды и запишите его в тест-план. Пользы от спора о словарях меньше, чем от общего понимания.

Смоук — первый кандидат на автоматизацию

Смоук стабилен, повторяется десятки раз в неделю и имеет чёткий оракул — это идеальный профиль для скрипта. Ручным он остаётся ровно до момента, когда команда деплоит чаще, чем человек успевает его проходить.

// smoke.spec.ts — автоматизированный аналог первых пунктов чек-листа.
// Тег @smoke позволяет гонять только этот набор на каждом деплое.
import { test, expect } from '@playwright/test';

test.describe('@smoke критичный путь покупки', () => {
  test('health-эндпоинт отдаёт ожидаемую версию', async ({ request }) => {
    const res = await request.get('/health');
    expect(res.status()).toBe(200);
    const body = await res.json();
    expect(body.version).toBe(process.env.RELEASE_VERSION);
    expect(body.db).toBe('ok');
  });

  test('гость доходит от поиска до страницы оплаты', async ({ page }) => {
    const errors: string[] = [];
    // Консольные ошибки — дешёвый оракул, которого нет в ручном чек-листе по умолчанию.
    page.on('pageerror', (e) => errors.push(e.message));

    await page.goto('/');
    await page.getByRole('searchbox').fill('кофе');
    await page.getByRole('searchbox').press('Enter');
    await expect(page.getByTestId('product-card').first()).toBeVisible();

    await page.getByTestId('product-card').first().click();
    await page.getByRole('button', { name: 'В корзину' }).click();
    await expect(page.getByTestId('cart-count')).toHaveText('1');

    await page.goto('/checkout');
    await expect(page.getByTestId('payment-form')).toBeVisible();
    expect(errors, `JS-ошибки на пути: ${errors.join('; ')}`).toHaveLength(0);
  });
});

Детали борьбы с хрупкостью таких тестов — в https://courses.digitable.life/post/testing/07-e2e-and-ui/. Здесь важно другое: даже автоматизированный смоук остаётся гейтом, и его роль в пайплайне не меняется.

# .github/workflows/deploy.yml — смоук как блокирующий шаг между стейджем и продом
jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Деплой на staging
        run: ./scripts/deploy.sh staging

  smoke:
    needs: deploy-staging
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx playwright install --with-deps chromium
      # Только смоук-набор: он должен укладываться в единицы минут.
      - name: Смоук на staging
        run: npx playwright test --grep @smoke
        env:
          BASE_URL: https://staging.example.com
          RELEASE_VERSION: ${{ github.sha }}
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: smoke-traces
          path: test-results/

  promote-to-prod:
    needs: smoke        # красный смоук => прод не получает билд
    if: success()
    runs-on: ubuntu-latest
    steps:
      - run: ./scripts/deploy.sh production

Подробнее про quality gates и параллелизацию — в https://courses.digitable.life/post/testing/13-tests-in-ci/.

4. Регрессионное тестирование: борьба с квадратичным ростом

Регресс отвечает на вопрос «не сломали ли мы то, что раньше работало». Проблема регресса не в сложности, а в арифметике. Каждый релиз добавляет функциональность, каждая функциональность добавляет сценарии, а время между релизами не растёт. Через два года у вас чек-лист на 900 пунктов и полторы недели на его прохождение — при недельном релизном цикле.

Дальше происходит одно из трёх, и все три плохи:

  • регресс проходят «по диагонали», проставляя галочки не глядя — метрика есть, информации нет;
  • регресс режут случайно («в этот раз проверим первую половину») — покрытие непредсказуемо;
  • релизы замедляются под регресс — бизнес начинает считать QA тормозом, и он прав.

Правильный ответ — не проходить весь регресс, а осознанно выбирать его подмножество. Это называется Regression Test Selection и опирается на две вещи: карту изменений и карту рисков.

Последний блок — самый недооценённый. Явно сообщить, что осталось непроверенным, — часть работы тестировщика. «Регресс пройден» без указания границ — дезинформация. Формулировка вида «прошли зоны оплаты и каталога полностью, партнёрский кабинет не трогали — там изменений не было, но он не проверялся три релиза» даёт менеджеру возможность принять осознанное решение.

Простой скрипт, который переводит эту логику в приоритетный список — полезнее, чем кажется, потому что делает выбор воспроизводимым и обсуждаемым:

"""Приоритизация областей регресса: риск = вероятность x влияние, с поправкой на изменения."""
from dataclasses import dataclass, field


@dataclass
class Area:
    name: str
    impact: int                 # цена отказа: 1 (косметика) .. 5 (потеря денег/данных)
    defect_history: int         # число дефектов за последние N релизов
    changed_now: bool           # затронут ли текущим диффом
    last_tested_ago: int        # сколько релизов назад проверяли
    manual_minutes: int         # цена ручного прохода
    covered_by_auto: bool = False

    def probability(self) -> float:
        """Вероятность дефекта: история + факт изменения + давность проверки."""
        p = 0.1 + 0.05 * min(self.defect_history, 8)   # база + вклад истории, потолок 0.5
        if self.changed_now:
            p += 0.35                                   # изменённый код ломается чаще всего
        p += 0.03 * min(self.last_tested_ago, 5)        # гниение непроверяемых зон
        return min(p, 0.95)

    def risk(self) -> float:
        r = self.probability() * self.impact
        if self.covered_by_auto:
            r *= 0.4        # автотесты снижают, но не обнуляют риск: у них свой оракул
        return r

    def value_per_minute(self) -> float:
        """Сколько риска снимаем за минуту ручной работы — по нему и сортируем."""
        return self.risk() / max(self.manual_minutes, 1)


def plan(areas: list[Area], budget_minutes: int) -> tuple[list[Area], list[Area]]:
    """Жадный отбор под бюджет: O(n log n) по времени, O(n) по памяти.

    Это вариант задачи о рюкзаке; жадность по «риск на минуту» не оптимальна
    формально, но на практике точнее любого интуитивного выбора и, главное,
    объяснима: всегда видно, почему область попала или не попала в план.
    """
    ordered = sorted(areas, key=Area.value_per_minute, reverse=True)
    selected, skipped, spent = [], [], 0
    for a in ordered:
        if spent + a.manual_minutes <= budget_minutes:
            selected.append(a)
            spent += a.manual_minutes
        else:
            skipped.append(a)
    return selected, skipped


areas = [
    Area("Оплата картой", impact=5, defect_history=6, changed_now=True,
         last_tested_ago=0, manual_minutes=45),
    Area("Регистрация", impact=4, defect_history=2, changed_now=False,
         last_tested_ago=1, manual_minutes=25, covered_by_auto=True),
    Area("Экспорт отчётов", impact=3, defect_history=5, changed_now=False,
         last_tested_ago=4, manual_minutes=30),
    Area("Настройки профиля", impact=1, defect_history=1, changed_now=False,
         last_tested_ago=3, manual_minutes=20),
    Area("Партнёрский кабинет", impact=4, defect_history=3, changed_now=True,
         last_tested_ago=2, manual_minutes=60),
]

selected, skipped = plan(areas, budget_minutes=120)
print("В план:", [a.name for a in selected])
print("НЕ проверяем (сообщить команде):", [(a.name, round(a.risk(), 2)) for a in skipped])

Вывод скрипта — не «правда», а основа для разговора. Значения impact и defect_history вы берёте из головы и из баг-трекера; ценность в том, что предположения становятся явными и их можно оспорить. Риск-ориентированный подход подробнее разобран в https://courses.digitable.life/post/testing/00-overview/; связь с решением «что автоматизировать» — в https://courses.digitable.life/post/testing/12-automation-strategy/.

Разработчику. Строка covered_by_auto умножает риск на 0.4, а не на ноль — и это принципиально. Ваши юнит- и интеграционные тесты (https://courses.digitable.life/post/testing/05-unit-testing/ — см. следующий раздел курса) закрывают логику, но не закрывают вёрстку, тексты, права доступа, состояния после миграций и всё, что живёт между слоями. «Покрыто автотестами» — это снижение вероятности, а не её устранение.

5. Исследовательское тестирование: главный навык профессии

Исследовательское тестирование (ET) — это одновременное обучение продукту, проектирование тестов и их выполнение. Определение Кема Канера, автора термина, звучит именно так: три деятельности сливаются в одну петлю, где результат предыдущего действия определяет следующее.

Главное недоразумение вокруг ET: его путают с ad-hoc-тыканьем. Разница — как между импровизацией джазового музыканта и случайным нажатием клавиш. Импровизация опирается на структуру, подготовку и рефлексию. ET — тоже.

Session-Based Test Management: как сделать ET управляемым

Главное возражение менеджмента против ET — «это неуправляемо, я не вижу, что происходит». Ответ на него придумали Джеймс и Джон Бах в 2000 году: SBTM (Session-Based Test Management). Идея — упаковать исследование в учётные единицы.

Единица работы — сессия: непрерывный таймбокс 45–120 минут (обычно 90) под одним чартером, без переключений на почту и митинги. Всё время сессии делится на три категории, которые называются метрикой T/B/S:

  • S (Setup) — подготовка окружения, данных, доступов;
  • T (Test design & execution) — собственно исследование;
  • B (Bug investigation & reporting) — воспроизведение, локализация, оформление находок.

Анатомия 90-минутной сессии исследовательского тестирования

Эта разбивка — не бюрократия, а диагностический инструмент. Если из сессии в сессию S съедает 40% времени, у вас проблема не с тестированием, а с тестовыми данными и окружением, и её решение окупится быстрее любого автотеста. Если B стабильно 50% — продукт сыпется, и стоит говорить с командой о качестве входящего билда, а не наращивать часы QA.

Чартер — миссия сессии на одно-два предложения. Хороший шаблон (Элизабет Хендриксон, «Explore It!»):

Исследовать (целевая область) с помощью (ресурсы, техники, данные) чтобы обнаружить (какого рода информацию).

Примеры рабочих чартеров:

  • Исследовать импорт CSV с помощью битых, огромных и экзотически закодированных файлов, чтобы обнаружить потерю данных и зависания UI.
  • Исследовать права ролей admin/manager/viewer с помощью прямых запросов к API в обход UI, чтобы обнаружить обход авторизации.
  • Исследовать корзину с помощью двух вкладок одного пользователя, чтобы обнаружить гонки и рассинхронизацию состояния.

Сравните с плохим чартером «протестировать корзину»: у него нет ни ресурса, ни типа искомой информации, и по нему невозможно понять, когда сессия закончена.

Отчёт сессии

Отчёт — не пересказ действий, а сжатая информация для команды. Формат, который реально читают:

# Session Report

CHARTER
  Исследовать импорт CSV с помощью битых и огромных файлов,
  чтобы обнаружить потерю данных и зависания UI.

BUILD / ENV
  RC-2.14 (sha 8f21a0c), staging, Chrome 138, macOS 15.2

AREAS
  ui/import-dialog, api/POST /v1/import, worker/csv-parser, db/staging_rows

START / DURATION
  2026-07-16 11:00 / 90 мин

TASK BREAKDOWN
  S 20% | T 50% | B 30%
  On-charter 85% / Off-charter 15% (ушёл в экспорт — там смежный риск)

DATA FILES
  fixtures/csv/*.csv (см. вложение), генератор gen_csv.py

BUGS
  BUG-412  Файл 200 МБ вешает вкладку без индикатора, таймаута нет
  BUG-413  UTF-8 BOM превращает первую колонку в «п»»їid», строки молча теряются

ISSUES (не баги, а препятствия и вопросы)
  - Нет способа увидеть статус импорта после закрытия вкладки
  - Непонятно, каково ожидаемое поведение для дублей по внешнему ключу — спросить у продакта
  - На staging нельзя загрузить файл > 250 МБ (лимит nginx), риск не проверен

NOTES
  Парсер разваливается на кавычках внутри кавычек (RFC 4180 §2.7) —
  не воспроизвёл стабильно, нужен отдельный чартер.

Строка ISSUES часто ценнее строки BUGS: она содержит риски, которые ещё не стали дефектами, и вопросы к спецификации. Именно там появляется работа для аналитика и продакта.

Эвристики: чем заполняется голова во время сессии

Исследование без техник вырождается в хаотичное кликанье. Три набора, которые стоит знать наизусть.

SFDIPOT (San Francisco Depot) — модель продукта Бача: по каким осям вообще можно его разрезать.

Ось Что смотреть
S Structure Из чего собран: файлы, модули, сервисы, железо
F Function Что умеет: расчёты, вычисления, обработка ошибок, UI
D Data Что обрабатывает: входы, выходы, размеры, кодировки, время жизни, кардинальность
I Interfaces Как с ним говорят: UI, API, файлы, импорт/экспорт
P Platform На чём живёт: ОС, браузеры, сеть, зависимости, локали
O Operations Как используют: типичные и экстремальные сценарии, роли
T Time Как влияет время: одновременность, таймауты, часовые пояса, порядок

Практика: выпишите семь букв в столбик и задайте по одному вопросу к каждой. Ось Time и ось Data дают больше всего продакшн-багов и чаще всего пропускаются.

Туры Уиттакера (из «Exploratory Software Testing») — способы обойти продукт с заданной ролью:

  • Guidebook tour — делаете строго то, что написано в документации. Расхождения — баги (оракул Claims).
  • Money tour — проходите только тем путём, которым приходят деньги. Здесь самая высокая цена дефекта.
  • Landmark tour — выбираете 5–7 «достопримечательностей» и ходите между ними в случайном порядке. Ломает предположения о линейности потока.
  • Back alley tour — идёте в самые непопулярные фичи. Там меньше всего тестировали.
  • Supermodel tour — смотрите только на внешний вид, не на логику: вёрстка, тексты, состояния загрузки.
  • Saboteur / anti-social tour — делаете всё, чего от вас не ждут: назад в браузере, двойные клики, отключение сети посреди операции, ввод emoji в поле «возраст».

Галлюмбир (Goldilocks) и границы — на любое поле применяйте «слишком мало / в самый раз / слишком много», а на любую последовательность — «прервать посередине». Это дёшево, механично и находит огромную долю дефектов. Формальная версия этой техники — граничные значения из https://courses.digitable.life/post/testing/02-test-design/.

Парное и групповое исследование

Два приёма, которые дают непропорционально много:

  • Парное тестирование (тестировщик + разработчик за одним экраном, 60 минут). Тестировщик видит странность, разработчик тут же смотрит в код и говорит «а, здесь у нас race condition» — цикл обратной связи схлопывается с дней до секунд. Побочный эффект: разработчик перестаёт считать QA формальностью.
  • Bug bash — вся команда (включая продакта, дизайнера, поддержку) на 60–90 минут садится ломать фичу перед релизом, каждый со своим чартером. Люди с разным контекстом видят разное; поддержка находит то, чего не видит никто, потому что помнит реальные обращения.

6. Приёмочное тестирование: вопрос про деньги

Приёмочное тестирование (UAT, User Acceptance Testing) отвечает не на вопрос «есть ли баги», а на вопрос «принимаем ли мы эту работу». Отсюда все его особенности:

  • решение принимает заказчик или его представитель, не QA и не разработка;
  • проверяются бизнес-сценарии целиком, а не функции по отдельности;
  • критерии приёмки формулируются до начала разработки — иначе приёмка превращается в торг;
  • дефекты, найденные здесь, стоят максимально дорого: продукт уже сделан.

Ключевой момент этой схемы — шаг «разделить: дефекты vs пожелания». На UAT всегда всплывают идеи «а хорошо бы ещё». Если не разделять их с дефектами, приёмка не заканчивается никогда. Практическое правило: дефект — это расхождение с зафиксированным критерием; всё остальное — новая работа и идёт в бэклог с обычной приоритизацией.

Критерии приёмки удобно писать в формате Given/When/Then — тот же язык, что используется в BDD (https://courses.digitable.life/post/testing/14-tdd-and-bdd/), даже если Cucumber вы не берёте:

Функция: Возврат средств по заказу

  Сценарий: Полный возврат в течение 14 дней
    Дано заказ №A-1042 оплачен картой 10 дней назад на сумму 12 480 ₽
    И статус заказа «Доставлен»
    Когда оператор оформляет полный возврат с причиной «Не подошёл размер»
    Тогда в платёжной системе создаётся возврат на 12 480    И статус заказа меняется на «Возврат оформлен»
    И клиенту уходит письмо с суммой и сроком зачисления
    И в бухгалтерской выгрузке за день появляется соответствующая строка

Обратите внимание на последнюю строку: приёмочные критерии почти всегда выходят за пределы UI. Проверка «письмо ушло» и «выгрузка сошлась» — это и есть отличие приёмки от функционального теста.

Соседние формы приёмки

  • Альфа — тестирование силами внутренних пользователей на рабочем окружении.
  • Бета — ограниченная группа реальных пользователей. Главная ценность — не поиск багов, а обнаружение того, что люди используют продукт не так, как вы задумали.
  • Operational Acceptance Testing (OAT) — приёмка со стороны эксплуатации: разворачивается ли, откатывается ли, есть ли мониторинг, работает ли бэкап и восстановление из него. Регулярно забывают, а потом узнают о проблеме в три часа ночи. Смежная тема — https://courses.digitable.life/post/devops/00-overview/.
  • Приёмка по контракту — когда критерии зафиксированы юридически, и от вердикта зависит оплата этапа. Здесь особенно важно, чтобы критерии были измеримыми: «система должна работать быстро» — не критерий, «95-й перцентиль ответа поиска < 800 мс при 200 RPS» — критерий (как это меряют — в https://courses.digitable.life/post/testing/09-performance-testing/).

7. Что оставлять человеку: экономика решения

Ручной час стоит денег и не масштабируется. Автоматизация стоит денег на разработку и на поддержку, но масштабируется. Решение принимается по двум осям: как часто проверка повторяется и насколько стабилен её оракул.

Грубая, но работающая прикидка окупаемости. Ручной прогон сценария стоит M минут и повторяется N раз в год. Автоматизация того же сценария — A минут разработки плюс S минут поддержки за прогон (починка селекторов, обновление данных, разбор флаки). Автоматизация выгодна, когда N × M > A + N × S. Отсюда два часто игнорируемых следствия.

Первое. При S, сравнимом с M, автоматизация не окупается никогда. Хрупкий E2E-тест, который каждый третий прогон требует пятиминутного разбора «упало по делу или флак», может стоить дороже ручной проверки. Это не аргумент против автоматизации — это аргумент за то, чтобы считать S честно.

Второе. Сценарий, который выполняется дважды в год, автоматизировать почти никогда не нужно, даже если это технически легко. Классический пример — ежегодная процедура закрытия финансового года.

Разработчику vs тестировщику: где взгляды расходятся. Разработчик почти всегда переоценивает покрытие автотестами и недооценивает стоимость поддержки — потому что видит логику, а не продукт целиком. Тестировщик чаще недооценивает выгоду автоматизации регресса и переоценивает ценность ручного повторения — потому что ручной прогон ощущается как «настоящая работа». Полезное упражнение для обеих сторон: разработчик проводит одну 90-минутную исследовательскую сессию по чужой фиче, тестировщик читает и правит один автотест. После этого спор становится содержательным.

8. Типичные ошибки

  1. Ручной регресс без бюджета и приоритетов. Приводит к «прохождению по диагонали». Лечится риск-ранжированием и явным сообщением о непроверенном.
  2. Смоук, который нельзя провалить. Если после красного смоука тестирование всё равно продолжается — это не гейт, а ритуал. Уберите его или дайте ему право блокировать.
  3. ET без чартера и таймбокса. Превращается в бесцельное кликанье, о котором нельзя отчитаться. Полтора часа под конкретной миссией дают больше, чем день свободного плавания.
  4. Пошаговые сценарии там, где нужен чек-лист. Инструкция «нажмите → введите → нажмите» отключает мышление: человек выполняет шаги и не смотрит по сторонам. Для опытного тестировщика формулируйте цель, а не маршрут.
  5. Отчёт «проверил, всё ок». Не содержит информации. Что именно проверено, на каком билде, в каком окружении, что осталось за кадром — вот содержание отчёта.
  6. Тестирование на «удобных» данных. Пользователь Тест Тестов с адресом ул. Тестовая, 1 никогда не найдёт баг с фамилией из двух слов, апострофом в O'Brien, адресом на 200 символов или именем на арабском. Заведите набор «злых» данных и используйте его всегда.
  7. Приёмка без заранее зафиксированных критериев. Превращается в переговоры о том, что вообще считать готовым.
  8. Ручная проверка того, что уже проверяет CI. Двойная оплата за одну и ту же информацию. Перед каждым ручным сценарием стоит спросить: «этого правда нет ни в одном автотесте?»

9. Инструменты, которые превращают клики в информацию

Ручное тестирование «мышкой и глазами» — самый слабый его вариант. Минимальный набор, отличающий инженера от кликера:

  • DevTools: вкладка Network (какой запрос ушёл, что вернулось, сколько занял), Console (JS-ошибки), Application (куки, localStorage, сессия), device toolbar и троттлинг сети — половина находок «фронтендных» багов на самом деле находится здесь.
  • HTTP-проксиProxyman, Charles, mitmproxy. Позволяют подменить ответ сервера и посмотреть, как UI переживёт 500, пустой массив или таймаут. Проверки, недоступные через интерфейс.
  • Логи и трейсы — доступ к логам приложения во время сессии превращает «что-то не сработало» в «упало вот здесь со вот этим исключением». Разница в скорости починки — на порядок.
  • SQL-доступ к тестовой базе — проверить, что реально записалось, а не что показал экран.
  • Генераторы данныхFaker, собственные скрипты. Ручной ввод двадцати записей — потерянное время.
  • Инструменты записи: скринкаст (баг с гонкой невозможно описать словами), сбор HAR-файла, Playwright trace viewer — прикладывать к баг-репорту.
  • Заметки — от текстового файла до mind map. Сессия без заметок теряет 70% находок: память не удерживает контекст исследования дольше нескольких минут.

Мини-итог

  • Автоматизировать можно только уже сформулированные ожидания. Человек нужен там, где оракул нечёткий: проблема оракула — фундаментальная, а не временная.
  • Смоук — короткий широкий гейт с правом блокировать; отвечает «билд пригоден?». Первый кандидат на автоматизацию.
  • Регресс растёт неограниченно; его не проходят целиком, а выбирают по риску и явно сообщают, что осталось непроверенным.
  • Исследовательское тестирование — не хаос, а управляемая практика: чартер, таймбокс, сессия, метрика S/T/B, отчёт, дебриф. Эвристики HICCUPPS, SFDIPOT и туры — рабочий инструментарий, а не теория.
  • Приёмка отвечает на вопрос о деньгах, решается заказчиком и требует критериев, зафиксированных до разработки.
  • Решение «руками или скриптом» — экономическое: N × M > A + N × S, где S (поддержка) почти всегда недооценивают.

Что дальше

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

Источники и что почитать:

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов