Тестирование ПО: карта курса, зачем оно нужно и сколько стоит его отсутствие
Это вход в трек о профессии тестировщика и об инженерной дисциплине качества. Дальше будет много конкретики: классы эквивалентности, Playwright, k6, тестконтейнеры, quality gates, баг-репорты, которые действительно чинят. Но если начать с инструментов, получится набор рецептов без критерия «зачем». Эта статья даёт критерий: тестирование — это покупка информации о рисках, и у этой покупки есть цена. Всё остальное в курсе — про то, как получить больше информации за меньшие деньги.
Курс написан так, чтобы быть полезным двум разным людям одновременно: тестировщику, для которого качество — профессия, и разработчику, для которого качество — часть работы над своим кодом. Там, где их взгляды расходятся (а расходятся они чаще, чем кажется), я буду это отмечать явно.
1. Что такое тестирование, если убрать ритуал
Самое распространённое определение — «тестирование ищет ошибки». Оно верное, но бесполезное: из него не следует, что делать, когда ошибок не нашли. Более рабочее определение дал Джеймс Бах:
Тестирование — это исследование продукта с целью предоставить заинтересованным лицам информацию о его качестве.
Ключевое слово — информация. Вы не «повышаете качество», прогоняя тесты: качество создаётся при проектировании и написании кода, тесты его только измеряют. Термометр не лечит. Отсюда сразу два практических следствия.
Следствие первое. Тест, результат которого вы можете предсказать заранее с уверенностью 100%, не даёт информации и не имеет ценности. Именно поэтому тысяча тестов на геттеры — это не «хорошее покрытие», это шум. Ценность теста пропорциональна вашей неуверенности в его исходе, умноженной на цену ошибки в этом месте.
Следствие второе. «Тесты прошли» не значит «багов нет». Это значит: «в проверенных местах ожидаемое поведение совпало с фактическим». Классическая формулировка Дейкстры (доклад 1969 года, разбор в EWD249):
Тестирование программ может показать наличие ошибок, но никогда — их отсутствие.
Это не пессимизм, а математический факт, и понимать его нужно буквально.
2. Почему полное тестирование невозможно в принципе
Возьмём функцию, честнее которой не бывает:
def price_with_discount(base: int, percent: int, coupon: int) -> int:
"""Три 32-битных целых на входе — куда уж проще."""
...
Пространство входов: 2**32 * 2**32 * 2**32 = 2**96 комбинаций — примерно 7.9e28. Если прогонять миллиард
входов в секунду, полный перебор займёт больше 2.5e12 лет: в сто раз дольше возраста Вселенной. И это функция
из трёх чисел, без состояния, без базы, без времени, без сети.
Реальный экран оформления заказа добавляет к этому: состояние корзины, историю пользователя, роль, локаль, часовой пояс, версию браузера, скорость сети, гонки между кликами. Число состояний растёт мультипликативно, а бюджет на тестирование — в лучшем случае линейно.
Отсюда центральная задача профессии, которой посвящён весь курс:
Из невообразимо большого пространства возможных проверок выбрать маленькое подмножество, которое находит наибольшую долю значимых дефектов.
Всё дальнейшее — техники такого выбора. Классы эквивалентности и границы (https://courses.digitable.life/post/testing/02-test-design/) — способ сжать пространство входов. Риск-ориентированный подход — способ приоритизировать области. Пирамида тестов (https://courses.digitable.life/post/testing/12-automation-strategy/) — способ выбрать уровень, на котором проверка дешевле. Ни одна из этих техник не даёт гарантий; все они меняют соотношение «найденные дефекты на потраченный час».
3. Сколько стоит отсутствие тестирования
Здесь важно не скатиться в проповедь. Считаем.
3.1. Стоимость дефекта растёт с фазой обнаружения
Идея восходит к Барри Боэму («Software Engineering Economics», 1981) и известна как «правило десяти». Честная оговорка, которую редко делают: точные множители многократно критиковались — например, в разборе «The ‘Leprechauns of Software Engineering’» Лорана Буньона показано, что часть цитируемых чисел пришла из очень маленьких выборок 1970-х. Но направление эффекта не оспаривается никем, и объясняется оно механически:
- на этапе требований дефект — это неверный абзац, правка стоит десять минут;
- в коде — это правка плюс ревью, но контекст ещё в голове автора;
- на тестировании — добавляются воспроизведение, оформление, коммуникация, повторный цикл сборки;
- в продакшне — добавляются инцидент, дежурный, откат, миграция уже испорченных данных, поддержка, а иногда регуляторы и суд.
Растёт не сложность самой правки, а стоимость всего, что вокруг неё.
3.2. Что бывает, когда не тестируют
Не абстракции, а публичные разборы, которые стоит прочитать целиком:
| Случай | Суть дефекта | Цена |
|---|---|---|
| Knight Capital, 2012 | Деплой на 8 серверов из 8, флаг переиспользован под новую логику, старый код остался на одном | ~440 млн долларов за 45 минут, компания продана (разбор SEC) |
| Ariane 5, рейс 501, 1996 | Конвертация 64-битного float в 16-битный int, переполнение, код перенесён с Ariane 4 без ретеста профиля полёта | Ракета и груз, ~370 млн долларов (отчёт комиссии) |
| Therac-25, 1985–1987 | Гонка при быстром вводе оператора, снят аппаратный предохранитель, тестировали только сборку целиком | Смерти пациентов; классический кейс Нэнси Левесон |
| CrowdStrike, 2024 | Обновление контент-конфига с несоответствием числа полей, валидатор пропустил, не было поэтапной раскатки | ~8.5 млн машин Windows, миллиарды убытков у клиентов |
Общая черта не в том, что «не написали юнит-тест». Общая черта — отсутствовал механизм, который поймал бы конкретный класс риска: непроверенный сценарий деплоя, непроверенный профиль нагрузки, непроверенная конкурентность, отсутствие канареечной раскатки. Тестирование — это не только запуск тестов; это ещё и конструкция процесса выпуска (https://courses.digitable.life/post/testing/13-tests-in-ci/ и трек DevOps).
3.3. Стоимость самого тестирования тоже существует
Обратная крайность так же вредна. Тесты нужно писать, ждать, чинить и переписывать при каждом рефакторинге. Набор из 4000 хрупких E2E-тестов, идущий два часа и падающий в 15% прогонов без причины, — это не актив, а техдолг с процентами: он тормозит релизы и, что хуже, приучает команду игнорировать красный билд.
Практический вывод из картинки: оптимум свой для каждого модуля. Для расчёта комиссии в биллинге разумный уровень — почти исчерпывающие юнит- и property-based тесты. Для админской страницы с настройкой цвета темы разумный уровень — один смоук-тест или вообще ничего. Одинаковый порог покрытия на весь репозиторий — самый надёжный способ промахнуться мимо оптимума одновременно в обе стороны.
4. Как решать, что тестировать: риск-ориентированный подход
Это главный навык, который отличает инженера качества от «прокликивателя». Основа — классическое определение риска:
риск = вероятность отказа × ущерб от отказа
Обе величины оцениваются экспертно, по шкале 1–5, и это нормально: цель — не точное число, а упорядочивание.
Как оценивать вероятность отказа, если данных нет. Хорошие эвристики, проверяемые фактами из репозитория:
- История дефектов. Модуль, где багов было много, — там их будет много и дальше. Кластеризация дефектов эмпирически устойчива.
- Частота изменений.
git log --format=%n --name-only --since=6.months | sort | uniq -c | sort -rn | head -20даёт список файлов, которые меняются чаще всего. Это карта риска почти бесплатно. - Сложность. Высокая цикломатическая сложность и глубокая вложенность условий — больше непроверенных путей.
- Новизна и «кто писал». Свежий код, код после срочного хотфикса, код уволившегося автора.
- Число интеграций. Каждая внешняя система — источник отказов, которые вы не контролируете.
- Конкурентность и время. Всё, что связано с параллелизмом, таймзонами и повторными попытками.
Как оценивать ущерб — это разговор не с разработчиком, а с бизнесом: деньги, юридические последствия (персональные данные, финансовая отчётность), безопасность людей, репутация, количество затронутых пользователей, обратимость (можно ли починить данные постфактум).
если сломается?} B -->|Деньги, данные, право, здоровье| C[Высокий приоритет] B -->|Неудобство, косметика| D[Низкий приоритет] C --> E{Можно ли проверить
на юнит-уровне?} E -->|Да| F[Юнит-тесты + property-based
дёшево, быстро, стабильно] E -->|Нет, нужна интеграция| G{Можно ли поднять
зависимость в контейнере?} G -->|Да| H[Интеграционные тесты
Testcontainers, контрактные тесты] G -->|Нет| I[E2E: только критический путь,
3-10 сценариев, не больше] D --> J{Часто ломается
исторически?} J -->|Да| K[Один смоук-тест] J -->|Нет| L[Не автоматизируем.
Исследовательская сессия раз в релиз] F --> M[Остаточный риск закрываем
мониторингом и алертами в проде] H --> M I --> M K --> M
Отдельно про последний узел. Часть риска дешевле не ловить тестами, а обнаруживать в проде за минуты: алерт на рост 5xx, на падение конверсии оплаты, на аномалию в очереди. Это не поражение тестирования, а осознанный перенос затрат туда, где они ниже. Тестировщик, который умеет сказать «эту проверку автоматизировать дороже, чем поставить алерт», стоит дорого.
5. Два взгляда: разработчик и тестировщик
Здесь курс отличается от учебников, написанных с одной стороны. Оба взгляда правильные, и оба неполные.
| Вопрос | Разработчик | Тестировщик |
|---|---|---|
| Цель по умолчанию | Показать, что код работает | Показать, при каких условиях он не работает |
| Основная единица | Функция, класс, модуль | Пользовательский сценарий, поток данных |
| Знание системы | Белый ящик: видит ветвления и может целиться в них | Чёрный/серый ящик: видит требования и поведение |
| Слепое пятно | Проверяет ровно те случаи, которые предусмотрел при написании — те же предположения, тот же баг | Не видит внутренних путей, пропускает то, что не проявляется в UI |
| Отношение к покрытию | Метрика прогресса | Метрика бесполезная без анализа рисков |
| Отношение к падению теста | «Тест сломался, поправлю тест» | «Тест что-то нашёл, разберусь, что именно» |
| Любимый уровень | Юнит: быстро, детерминированно, в IDE | Системный: ближе к тому, что видит пользователь |
| Что легко упускает | Требования, граничные бизнес-правила, UX | Конкурентность, обработку ошибок, производительность кода |
Самое ценное расхождение — предпоследняя строка. Разработчик, тестирующий свой код, воспроизводит в тестах ту же ментальную модель, из-за ошибки в которой и появился дефект. Это не лень и не непрофессионализм, это когнитивное ограничение: подтверждающее смещение. Именно поэтому даже в командах со стопроцентной автоматизацией сохраняется ценность второй пары глаз — исследовательского тестирования (https://courses.digitable.life/post/testing/04-manual-testing/).
Обратное тоже верно: тестировщик без доступа к коду не проверит retry-логику, идемпотентность обработчика очереди или поведение при частичном отказе зависимости. Отсюда современный формат — тестировщик участвует в обсуждении до написания кода, а разработчик пишет тесты на своём уровне.
6. Сквозной пример: от риска до строчки в CI
Возьмём одну маленькую фичу и пройдём весь путь. Требование:
Промокод даёт скидку в процентах. Скидка не может превышать 50% от суммы заказа. Промокод действует до даты окончания включительно. Просроченный или неизвестный код — заказ без скидки.
6.1. Анализ рисков и вопросы к требованию
Первое, что делает опытный тестировщик, — не пишет тесты, а задаёт вопросы. Каждый вопрос ниже дешевле задать сейчас, чем поймать дефектом потом:
- «до даты включительно» — в чьей таймзоне? Сервера, пользователя, UTC?
- скидка 60% в базе — ошибка конфигурации или её надо обрезать до 50%?
- округление: 3 копейки скидки — вверх, вниз, банковское?
- сумма 0 при 100% скидке — заказ вообще создаётся?
- код применили дважды в двух вкладках одновременно — что произойдёт?
Последний вопрос — про конкурентность, и его почти никогда не задают. А именно он даёт самые дорогие production-инциденты.
6.2. Чек-лист (перед формальными кейсами)
Чек-лист дешевле тест-кейса и годится, когда сценарий понятен исполнителю. Подробно — в https://courses.digitable.life/post/testing/03-documentation/.
Промокоды — чек-лист
[ ] валидный код, скидка < 50% → скидка применена, сумма пересчитана
[ ] валидный код, скидка = 50% → применена полностью (граница)
[ ] валидный код, скидка = 51% → обрезана до 50% (за границей)
[ ] код истёк вчера → скидки нет, понятное сообщение
[ ] код истекает сегодня 23:59:59 → скидка есть (граница, включительно)
[ ] неизвестный код → скидки нет, сообщение не раскрывает существование кодов
[ ] пустая строка / пробелы / регистр → нормализация, без 500
[ ] код применён дважды подряд → повторно не суммируется
[ ] две вкладки, одновременное применение → итоговая сумма консистентна
[ ] сумма заказа 0 → нет деления на ноль, нет отрицательной суммы
6.3. Формальный тест-кейс на граничный случай
ID: PROMO-007
Название: Скидка выше лимита обрезается до 50%
Приоритет: High (влияет на деньги)
Предусловия:
1. Существует активный промокод SUMMER60 со скидкой 60%, срок до 2026-12-31.
2. В корзине один товар стоимостью 1000.00 RUB.
Шаги:
1. Открыть корзину.
2. Ввести в поле промокода "SUMMER60" и нажать «Применить».
Ожидаемый результат:
- Отображается скидка 500.00 RUB (50%, а не 600.00).
- Итог к оплате: 500.00 RUB.
- В ответе POST /api/cart/promo поле discount_amount = 50000 (копейки).
Обратите внимание: ожидаемый результат сформулирован проверяемо и на двух уровнях сразу — UI и API. «Скидка применяется корректно» — не ожидаемый результат, а отказ от мышления.
6.4. Юнит-тесты (Python, pytest)
from datetime import date
import pytest
from shop.pricing import apply_promo, PromoCode
MAX_DISCOUNT_PCT = 50
@pytest.mark.parametrize(
"percent, expected_discount",
[
(0, 0), # нижняя граница: скидки нет
(1, 1000), # минимальная ненулевая (копейки: 1% от 100 000)
(49, 49000), # чуть ниже лимита
(50, 50000), # ровно лимит — граничное значение
(51, 50000), # за лимитом — обрезаем
(100, 50000), # верхняя граница диапазона
],
)
def test_discount_is_capped_at_50_percent(percent, expected_discount):
"""Границы вокруг бизнес-правила «не более 50%» — классические boundary values."""
promo = PromoCode(code="X", percent=percent, valid_until=date(2026, 12, 31))
result = apply_promo(total_kopecks=100_000, promo=promo, today=date(2026, 7, 16))
assert result.discount_kopecks == expected_discount
assert result.total_kopecks == 100_000 - expected_discount
@pytest.mark.parametrize(
"today, expected_applied",
[
(date(2026, 7, 15), True), # до даты окончания
(date(2026, 7, 16), True), # в день окончания — «включительно»
(date(2026, 7, 17), False), # после — не действует
],
)
def test_expiry_is_inclusive(today, expected_applied):
"""Тест кодирует ответ на вопрос из 6.1: граница «включительно» зафиксирована."""
promo = PromoCode(code="X", percent=10, valid_until=date(2026, 7, 16))
result = apply_promo(total_kopecks=100_000, promo=promo, today=today)
assert (result.discount_kopecks > 0) is expected_applied
def test_никогда_не_уходим_в_минус():
"""Инвариант, который должен выполняться при любых входах."""
promo = PromoCode(code="X", percent=100, valid_until=date(2026, 12, 31))
result = apply_promo(total_kopecks=0, promo=promo, today=date(2026, 7, 16))
assert result.total_kopecks == 0
assert result.discount_kopecks == 0
Стоимость такого набора — минут двадцать, время прогона — миллисекунды, стабильность — стопроцентная. Это тот уровень, где тестирование почти бесплатно; подробности — в https://courses.digitable.life/post/testing/05-unit-testing/.
6.5. E2E-тест на критический путь (TypeScript, Playwright)
E2E дорог, поэтому его пишут только на сценарии из первого квадранта матрицы рисков.
import { test, expect } from '@playwright/test';
test('промокод сверх лимита обрезается до 50% и это видно в корзине', async ({ page }) => {
// Состояние готовим через API, а не кликами: быстрее и не ломается от смены вёрстки логина.
await page.request.post('/test-api/seed', {
data: { promo: { code: 'SUMMER60', percent: 60 }, cart: [{ sku: 'A-1', price: 100000 }] },
});
await page.goto('/cart');
// Локаторы по ролям и data-testid, а не по CSS-классам — они переживут редизайн.
await page.getByTestId('promo-input').fill('SUMMER60');
await page.getByRole('button', { name: 'Применить' }).click();
// Веб-первые ассерты Playwright ждут условие сами: никаких waitForTimeout.
await expect(page.getByTestId('discount-amount')).toHaveText('500,00 ₽');
await expect(page.getByTestId('total-amount')).toHaveText('500,00 ₽');
});
Три решения в этих пятнадцати строках — противоядие от хрупкости: подготовка данных через API,
семантические локаторы, ожидание условия вместо sleep. Разбор — в https://courses.digitable.life/post/testing/07-e2e-and-ui/.
6.6. Баг-репорт, если что-то не так
Заголовок: Скидка по промокоду SUMMER60 применяется как 60% вместо ограничения в 50%
Окружение: stage, build 2026.7.16-rc3, Chrome 141, десктоп
Предусловия: промокод SUMMER60 (60%), корзина на 1000.00 RUB
Шаги:
1. Открыть /cart с товаром A-1 (1000.00 RUB)
2. Ввести SUMMER60, нажать «Применить»
Фактически: скидка 600.00 RUB, итог 400.00 RUB
Ожидается: скидка 500.00 RUB, итог 500.00 RUB (требование PRD-42, п.3: не более 50%)
Влияние: прямая потеря выручки 10% на каждом заказе с таким кодом.
В проде активны 4 кода со скидкой выше 50%.
Приложено: HAR, скриншот, ответ POST /api/cart/promo (discount_amount=60000), логи с trace_id=8f2c...
Что делает этот репорт чинибельным: воспроизводимость, разделение «факт / ожидание», ссылка на источник ожидания, оценка влияния в деньгах и артефакты для отладки. Формула «влияние в деньгах» решает споры о приоритете лучше любых аргументов.
6.7. Тот же пример в CI
name: quality-gates
on: [pull_request]
jobs:
fast: # обратная связь до 5 минут — иначе её не будут ждать
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.12" }
- run: pip install -r requirements-dev.txt
- run: ruff check . # статический анализ дешевле любого теста
- run: pytest tests/unit -q --maxfail=1
integration:
needs: fast # не тратим ресурсы, если базовое сломано
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pytest tests/integration -q # БД и брокер поднимаются Testcontainers
e2e-critical:
needs: fast
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4] # параллелизация: 40 минут → 10
steps:
- uses: actions/checkout@v4
- run: npx playwright test --grep @critical --shard=${{ matrix.shard }}/4
- uses: actions/upload-artifact@v4
if: failure()
with:
name: traces-${{ matrix.shard }}
path: test-results/ # трейсы и видео — иначе падение неразбираемо
Ключевая идея — раскладка по времени обратной связи: линтер за секунды, юниты за минуту, интеграция за
пять, E2E только по тегу @critical. Полный регресс уходит в ночной прогон. Подробно — в
https://courses.digitable.life/post/testing/13-tests-in-ci/.
7. Жизненный цикл дефекта
Общий язык, без которого невозможен разговор между ролями. Названия статусов в вашем трекере могут отличаться, логика — почти никогда.
не воспроизводится Новый --> Отложен: риск принят,
чинить сейчас дороже Новый --> Назначен: триаж определил приоритет Назначен --> ВРаботе: разработчик взял ВРаботе --> Исправлен: правка в ветке,
добавлен регрессионный тест Исправлен --> НаПроверке: попал в сборку НаПроверке --> Закрыт: проверено на исходных шагах
и вокруг них НаПроверке --> Переоткрыт: не починено
или сломано соседнее Переоткрыт --> Назначен Отклонён --> [*] Отложен --> [*] Закрыт --> [*]
Два места, где команды теряют больше всего времени. Первое — триаж: если приоритет назначается без оценки
ущерба, бэклог дефектов превращается в свалку, и через год там 900 задач, которые никто не откроет. Второе —
переход Исправлен → Закрыт без регрессионного теста: тот же баг вернётся через три релиза, и вы заплатите
за него второй раз.
8. Честно про боль
Курс был бы нечестным без этого раздела. Четыре вещи, которые в рекламных статьях не пишут.
Покрытие — метрика, которой легче всего обмануть себя. Вот тест, дающий 100% покрытия строк и нулевую ценность:
def test_ничего_не_проверяющий():
result = calculate_invoice(order) # все строки функции выполнены
assert result is not None # ассерт, не проверяющий ничего
Line coverage говорит только: «строка была выполнена». Не «результат был проверен» и не «поведение верное».
Мутационное тестирование ловит такие тесты: инструмент вносит в код мелкие мутации (> → >=, + → -)
и проверяет, что тесты падают. Если после мутации тесты зелёные — тест бесполезен. Это дороже покрытия,
но честнее. Разбор — в https://courses.digitable.life/post/testing/13-tests-in-ci/. Практический ориентир: покрытие полезно как
детектор дыр (какие ветки не выполнялись ни разу) и вредно как KPI (закон Гудхарта включается за неделю).
Флаки-тесты убивают доверие быстрее, чем баги. Тест, который падает в 2% прогонов, при наборе из 500 тестов делает красным почти каждый билд. Дальше команда учится перезапускать, а потом — игнорировать. И тогда настоящее падение проходит незамеченным. Флаки-тест хуже отсутствующего теста: отсутствующий честно молчит. Правило: флаки-тест либо чинится в течение дня, либо карантинится с задачей и дедлайном.
E2E хрупки структурно, а не по невезению. Тест, дергающий десять сервисов через браузер, зависит от каждого из них, от сети, от скорости CI-агента и от вёрстки. Вероятность зелёного прогона — произведение вероятностей. Поэтому E2E-набор не растят: его держат маленьким и вкладываются в стабильность каждого сценария.
Автоматизация не заменяет мышление. Автотест проверяет то, что вы уже придумали. Он никогда не заметит, что кнопка съехала за границу экрана, что формулировка ошибки оскорбительна или что новая фича противоречит старой. Полностью автоматизированная команда без исследовательских сессий систематически пропускает целые классы проблем.
9. Карта курса
Маршруты по курсу зависят от того, зачем вы пришли.
| Кто вы | Маршрут |
|---|---|
| Начинающий тестировщик | Подряд: 01 → 15. Особое внимание 02, 03, 04 — это ежедневная работа. |
| Разработчик, который хочет писать тесты лучше | 01 (термины) → 02 (тест-дизайн — самый недооценённый навык) → 05 → 06 → 12 → 13 → 14. |
| Автоматизатор | 02 → 05 → 06 → 07 → 08 → 12 → 13. Стратегия важнее фреймворка. |
| Тимлид или менеджер | Эта статья → 12 (ROI) → 13 (quality gates) → 15 (найм и грейды). |
| Готовитесь к собеседованию | 01, 02, 03 наизусть; затем 12 и 13 для вопросов «а как бы вы построили процесс». |
Языковые треки курса — Go, TypeScript, C#, Elixir — содержат разделы про тестирование в конкретной экосистеме. Здесь мы намеренно не дублируем детали фреймворков и говорим о том, что переносится между языками. Смежные темы: принципы разработки (тестируемость как следствие дизайна) и DevOps (конвейер, в котором живут тесты).
10. Мини-итог
- Тестирование — это покупка информации о рисках, а не ритуал и не гарантия. Ценность теста равна вашей неуверенности в исходе, умноженной на цену ошибки.
- Исчерпывающее тестирование невозможно: пространство входов растёт мультипликативно. Вся профессия — про умный выбор подмножества проверок.
- Стоимость дефекта растёт на порядки с фазой обнаружения. Точные множители спорны, направление — нет.
- У тестирования тоже есть цена. Оптимум — там, где минимальна сумма затрат на тесты и цены пропущенных дефектов, и он свой для каждого модуля.
- Приоритеты задаёт риск:
вероятность × ущерб. Историю изменений можно взять изgit logбесплатно. - Разработчик и тестировщик слепы по-разному, и именно поэтому нужны оба взгляда.
- Покрытие — детектор дыр, а не KPI. Флаки-тест хуже отсутствующего. E2E хрупки структурно.
Что можно сделать сегодня, до чтения остальных статей: выпишите пять функций вашего продукта, оцените для
каждой ущерб от отказа по шкале 1–5 и посмотрите git log по частоте изменений. Пересечение верхних строк
двух списков — это места, где ваши тесты нужны в первую очередь. Скорее всего, там их сейчас меньше всего.
Источники
- Гленфорд Майерс, «The Art of Software Testing» — классика, определение цели тестирования.
- Ліса Криспин, Джанет Грегори, «Agile Testing» — квадранты тестирования, роль QA в команде.
- Владимир Хориков, «Unit Testing Principles, Practices and Patterns» — что делает тест ценным.
- Барри Боэм, «Software Engineering Economics» (1981) — исходник «правила десяти».
- Лоран Буньон, «The Leprechauns of Software Engineering» — критический разбор популярных цифр отрасли.
- ISTQB Foundation Level Syllabus — общая терминология отрасли.
- Google Testing Blog — практика на большом масштабе, серия про флаки-тесты.
- Мартин Фаулер, «Test Pyramid» и «Eradicating Non-Determinism in Tests».
Что дальше
Принципы и терминология: верификация, валидация, дефект, качество — разберём, чем ошибка отличается от дефекта и отказа, что означают семь принципов тестирования на практике, почему «качество» распадается на разные атрибуты и как этот словарь спасает от бессмысленных споров в команде.