Зачем нужно тестирование

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

Тестирование — это способ получить информацию о качестве продукта, чтобы человек, отвечающий за релиз, мог принять осознанное решение: выпускать, доделывать или откладывать. Найденные дефекты — побочный продукт, а не цель.

Первое, что слышит новичок: «тестировщик ищет баги». Формально верно — примерно так же, как «врач выписывает таблетки». Таблетки — это то, что видно пациенту; работа врача — понять, что происходит, и снизить неопределённость. У тестировщика ровно так же. Багрепорт — видимый результат. Настоящий результат — ответ на вопрос «что мы знаем про эту версию продукта и чего мы про неё не знаем».

Чтобы этот тезис не звучал как красивая фраза с собеседования, начнём с денег.

Дефект дешевеет только в одну сторону — назад

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

Где нашли дефектЧто значит «исправить»Порядок цены
Требования, до кодаПереписать абзац в постановке, поспорить 15 минут на груминге×1
Дизайн и архитектураПерерисовать макет, пересогласовать схему данных×5
Разработка, до слияния веткиРазработчик правит свой же свежий код, контекст в голове×10
ТестированиеБагрепорт, переключение контекста разработчика, повторный прогон регресса, сдвиг сроков×15–40
ПродакшнХотфикс, откат, миграция испорченных данных, обращения в поддержку, репутация, иногда прямые убытки×100 и выше

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

Совсем конкретно. Аналитик написал «поле email — необязательное». Никто не задал вопрос. Дальше: разработчик сделал колонку nullable, маркетолог построил на этих данных рассылку, в базе накопилось 200 тысяч аккаунтов без почты. Через полгода бизнес говорит: «нам нужно восстановление пароля». Исправить это в требованиях стоило бы одного вопроса тестировщика: «а как человек будет восстанавливать доступ?» Исправить в продакшне — это миграция данных, кампания по дособиранию почт, поддержка, которая полгода будет разгребать «не могу войти».

Именно поэтому хороший тестировщик страшно любит задавать вопросы к тексту, а не только тыкать кнопки. Вопрос к требованию — самый дешёвый баг в мире.

Пять дефектов, которые стоили дороже целой карьеры

Эти истории стоит знать не ради эрудиции. В каждой видна конкретная дыра в тестировании, которую вы будете встречать всю жизнь — только в масштабе своего продукта.

СлучайЧто произошлоЦенаУрок для тестировщика
Therac-25, 1985–1987Аппарат лучевой терапии. Если оператор очень быстро правил введённые параметры, срабатывала гонка состояний (race condition) и пациент получал дозу в сотни раз выше назначенной. Аппаратные блокировки в новой модели убрали, положившись на программу.Тяжёлые поражения и смерти пациентовКомпоненты по отдельности «работали». Сценарий «оператор печатает быстро и ошибается» никто не проверял.
Ariane 5, полёт 501, 1996Инерциальную систему переиспользовали от Ariane 4. При конвертации 64-битного числа с плавающей точкой в 16-битное целое произошло переполнение, бортовой компьютер выдал мусор вместо навигации, ракета отклонилась и самоликвидировалась примерно на 40-й секунде.Около 370 млн долларовПереиспользованный код в НОВОМ контексте — это новый код. Старые тесты про новые условия ничего не знают.
Mars Climate Orbiter, 1999Один модуль считал импульс в фунт-силах-секундах, другой ожидал ньютон-секунды. Аппарат вошёл в атмосферу Марса слишком низко и погиб.Сотни миллионов долларов миссииКлассический интеграционный дефект: каждый модуль «правильный», ошибка живёт на стыке. Проверяйте контракты между системами.
Knight Capital, 2012При выкатке новый код попал на семь серверов из восьми. На восьмом ожил старый флаг, переиспользованный под новую функцию, и торговый робот начал безумно скупать и сбрасывать акции.Около 440 млн долларов за 45 минут; компания фактически перестала существоватьТестировать нужно не только код, но и процесс релиза, откат и работу в «смешанном» состоянии версий.
Heartbleed, OpenSSL, 2014Сервер не проверял, что заявленная длина в служебном запросе совпадает с реальной, и вежливо отдавал в ответ кусок собственной памяти — с ключами и паролями. Уязвимость прожила в коде около двух лет.Сотни тысяч затронутых серверов по всему миру«Работает» не равно «безопасно». Позитивные проверки эту дыру не видят — нужны негативные и граничные.

Обратите внимание: почти нигде причина не в «программист не умеет писать код». Причины — гонки, стыки, переиспользование, релизная процедура, отсутствие проверок на некорректный ввод. Всё это территория тестировщика.

Тестирование ничего не гарантирует — оно снижает неопределённость

Тестирование может показать наличие дефектов, но не может доказать их отсутствие. Эдсгер Дейкстра

Это не пессимизм, а математика. Представьте обычную форму регистрации: имя, email, пароль, возраст, страна, валюта, галочка согласия. Даже если для каждого поля взять не все возможные значения, а всего несколько классов («пусто», «валидное», «слишком длинное», «спецсимволы»), количество комбинаций взрывается.

# «Давайте просто проверим все комбинации» — считаем, во что это обойдётся
polya = {
    "имя": 4,
    "email": 5,
    "пароль": 6,
    "возраст": 5,
    "страна": 200,
    "валюта": 15,
    "согласие": 2,
}

kombinacii = 1
for pole, klassov in polya.items():
    kombinacii *= klassov

sekund_na_proverku = 30
vsego_sekund = kombinacii * sekund_na_proverku

print("Комбинаций:", kombinacii)
print("Часов ручной проверки:", round(vsego_sekund / 3600, 1))
print("Лет непрерывной работы:", round(vsego_sekund / (3600 * 24 * 365), 2))

Результат:

Комбинаций: 3600000
Часов ручной проверки: 30000.0
Лет непрерывной работы: 3.42

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

Как выбирать: риск = вероятность × ущерб

Инструмент выбора один и тот же на любом проекте. Прикидываем для каждой функции две вещи: насколько вероятно, что она сломана, и насколько больно будет, если она сломается у пользователя.

Функция интернет-магазинаВероятность дефектаУщербПриоритет
Оплата картойСредняя (много интеграций)Катастрофа: потерянные деньги и довериеP0 — проверяем всегда
Оформление заказаСредняяВысокий: нет заказа — нет выручкиP0
Поиск по каталогуВысокая (часто меняют)Средний: люди уйдут, но не сразуP1
История заказовНизкаяСреднийP2
Смена аватара в профилеНизкаяНизкийP3 — если останется время

Если вы потратили три часа на автотесты аватарки, пока оплата через новый эквайринг не проверена ни разу, — вы отработали честно и при этом абсолютно бесполезно.

Что на самом деле является продуктом вашей работы

Сравните две реплики тестировщика на релизной встрече.

Плохо:
  «Я всё протестировал, багов нет, можно катить.»

Хорошо:
  «Проверил 34 сценария из 40 запланированных. Критичных дефектов нет.
   Открыты два средних: не сохраняется адрес доставки при смене города,
   и на iOS съезжает кнопка оплаты (косметика).
   НЕ проверялась оплата через СБП — нет тестового контура.
   Это примерно 30% наших платежей: если там сломано, узнаем от клиентов.
   Решение о релизе — за вами.»

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

Как это работает

Любая проверка, от ручного клика до автотеста, устроена одинаково:

  1. Риск — предположение, что здесь может быть сломано.
  2. Выбор проверки — какой сценарий и с какими данными покажет, так это или нет.
  3. Выполнение — прогоняем.
  4. Наблюдение — что произошло на самом деле.
  5. Сравнение с оракулом — а как должно было быть?
  6. Интерпретация и отчёт — это дефект? насколько важный? что это говорит о качестве в целом?

Ключевой и самый недооценённый пункт — пятый. Оракул — это источник знания о том, каким должно быть правильное поведение. Оракулом может быть требование, макет, поведение прежней версии, аналогичный продукт конкурента, стандарт, закон, здравый смысл или внутренняя непротиворечивость («на карточке товара цена 990, а в корзине 1090 — что-то одно врёт»). Если у вас нет оракула, у вас нет теста: вы просто нажимаете на кнопки и смотрите, что будет. Это называется «потыкать», и это тоже иногда полезно, но путать одно с другим нельзя.

Частые ошибки

  • «За качество отвечает тестировщик». Нет. За качество отвечает вся команда, включая разработчиков, аналитика и продакта. Тестировщик отвечает за достоверность и полноту информации о качестве. Разница принципиальная: во втором случае вас невозможно сделать крайним за чужое решение «катим как есть».
  • «Мы всё протестировали». Так не бывает — см. расчёт выше. Говорите конкретно: что покрыто, что нет, что осталось за скобками.
  • Гнаться за числом найденных дефектов. Как только «баги в месяц» становятся метрикой, в трекер потоком идёт мусор про отступы, а тяжёлые сценарии никто не трогает — они долгие и в них можно ничего не найти.
  • Проверять только счастливый путь. Пользователь обязательно введёт минус в поле «количество», нажмёт «Оплатить» дважды и выключит Wi-Fi посередине. Heartbleed и Therac-25 — как раз про это.
  • Держать риски в голове. Не записанный и не озвученный риск для команды не существует. Если вы «вроде подозревали», но никому не сказали — вы не сделали свою работу.
  • Начинать тестировать, когда код готов. Самые дорогие дефекты рождаются в требованиях, а там ещё нечего кликать — там нужно читать и спрашивать.

Итоги

  • Тестирование существует не ради багов, а ради информации для решения о релизе.
  • Цена дефекта растёт нелинейно: дешевле всего он стоит в требованиях, дороже всего — у пользователя.
  • Исчерпывающее тестирование математически невозможно, поэтому главный навык — приоритизация по риску (вероятность × ущерб).
  • Каждая проверка требует оракула — понимания, как должно быть. Без него это не тест.
  • Хороший отчёт всегда говорит и о том, что не проверено, — это часть информации, а не признание в слабости.
  • Легендарные катастрофы почти всегда про стыки, гонки, переиспользование и релизные процедуры, а не про «плохой код».
Проверьте себя
1. Почему дефект, найденный на этапе требований, обходится компании дешевле того же дефекта в продакшне?
AПотому что в требованиях его проще заметить глазами
BПотому что поверх ошибки ещё ничего не построено: нет кода, тестов, интеграций и данных пользователей, которые придётся переделывать
CПотому что на раннем этапе дефекты всегда мелкие и некритичные
DПотому что за ранние дефекты не платят премию разработчикам
2. Тестировщик говорит на релизной встрече: «Я всё протестировал, багов нет». Что не так с этой фразой?
AНичего, это идеальный отчёт: команда получила зелёный свет
BОна слишком короткая, надо было перечислить все пройденные тест-кейсы
CИсчерпывающее тестирование невозможно, поэтому «всё» — неправда; и во фразе нет главного: что осталось непроверенным и какие есть риски
DТестировщик не имеет права высказываться о релизе