Тестировать поле возраста принимающее 18–65 случайными значениями вроде 25, 42 и 60 не даёт ничего чего не дало бы первое значение. Баги прячутся на краях: 17, 18, 65 и 66, точные значения где ошибка на единицу в условии проявляется. Статья разбирает пять систематических техник для выбора входных данных для тестирования: разбивку на классы эквивалентности, анализ граничных значений, таблицы решений, тестирование переходов состояний и попарное комбинаторное тестирование, с руководством по выбору подходящей техники в зависимости от типа фичи.

Разбивка на классы эквивалентности: перестань тестировать одно и то же дважды

Базовая идея проста: если два входных значения дают одинаковое поведение, тестировать нужно только одно из них. Полное пространство входных данных делится на группы (классы или разделы эквивалентности) где каждое значение в группе ведёт себя одинаково. Затем тестируется одно представительное значение из каждой группы.

Возьмём регистрационную форму с полем возраста. Требование говорит что пользователи должны быть в возрасте от 18 до 65. Можно тестировать каждое целое число от 0 до 120, но это 121 тест-кейс. Разбивка на классы эквивалентности показывает что важны только четыре группы:

| Раздел | Диапазон | Пример значения |

|--------|----------|----------------|

| Валидный | 18–65 | 35 |

| Слишком молодой (невалидный) | Меньше 18 | 10 |

| Слишком старый (невалидный) | Больше 65 | 80 |

| Неверный тип (невалидный) | Нечисловой | "twenty" |

Четыре тест-кейса вместо 121. Логика: если 35 проходит и 36 тоже проходит, тестирование 36 не даёт дополнительной информации. Если 10 корректно отклоняется, 11 тоже отклонится.

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

Анализ граничных значений: где реально прячутся баги

Разработчики делают ошибки на краях. Ошибка на единицу выглядит как if (age > 18) вместо if (age >= 18). Разбивка на классы находит разделы; анализ граничных значений (BVA) тестирует стены между ними.

Для того же поля возраста 18–65, BVA даёт такие тест-кейсы:

| Значение | Граница раздела | Ожидаемый результат |

|----------|----------------|---------------------|

| 17 | Чуть ниже нижней границы | Отклонён |

| 18 | Нижняя граница | Принят |

| 19 | Чуть выше нижней границы | Принят |

| 64 | Чуть ниже верхней границы | Принят |

| 65 | Верхняя граница | Принят |

| 66 | Чуть выше верхней границы | Отклонён |

Шесть тест-кейсов покрывающих обе границы с обеих сторон. В сочетании с разбивкой на классы эквивалентности (покрывающей середину и случай неверного типа) получается полный тест-сьют для этого поля из примерно 8–10 тест-кейсов.

BVA работает везде где есть граница: максимальная длина символов в текстовых полях, минимальное количество товаров в e-commerce, лимиты размеров файлов, диапазоны дат, количество страниц пагинации, API rate limit. Паттерн всегда одинаков: тестируй значение на границе, одно ниже и одно выше.

Когда находишь баг через BVA, он почти всегда точно на границе. Поле возраста которое отклоняет 65 но принимает 64 и 66 раскрывает ошибку на единицу. Укажи это конкретно в баг-репорте. "Сбой граничного условия на верхней границе" помогает разработчику найти быстрее.

Таблицы решений: делаем бизнес-логику видимой

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

Таблицы решений делают эту логику явной. Каждый столбец: тест-кейс. Каждая строка: условие или результат.

Возьмём фичу скидок: пользователи получают скидку если у них есть карта лояльности или если сумма покупки превышает 100 долларов. Комбинации выглядят так:

| | Кейс 1 | Кейс 2 | Кейс 3 | Кейс 4 |

|---|---|---|---|---|

| Есть карта лояльности | Нет | Да | Нет | Да |

| Покупка > $100 | Нет | Нет | Да | Да |

| Получает скидку | Нет | Да | Да | Да |

Это правило ИЛИ, поэтому три из четырёх кейсов дают скидку. Четыре тест-кейса, по одному на столбец. Если бы правило было И (нужна карта лояльности И покупка свыше $100), только Кейс 4 соответствовал бы. Таблица показала бы это немедленно, и ты знал бы что нужно тестировать все четыре комбинации для подтверждения логики.

Таблицы решений эффективны когда требования содержат слова "если", "когда", "если не", "кроме случая", "но только если". Эти слова сигнализируют что условная логика требует отображения. Таблица заставляет перечислить каждую комбинацию, что часто раскрывает случаи которые требования не специфицировали. Что происходит если у пользователя нет карты лояльности и заказ ровно $100? Таблица делает эту неоднозначность видимой до разработки, когда её дёшево разрешить.

Для фичи с тремя бинарными условиями: 2³ = 8 комбинаций. Для четырёх условий: 16. Таблицы решений не всегда требуют тестирования всех комбинаций (некоторые могут быть невозможными или нерелевантными), но они помогают видеть полное пространство до решения что сократить.

Тестирование переходов состояний: фичи которые помнят где побывали

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

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

Возьмём управление аккаунтом пользователя. Аккаунт может находиться в одном из трёх состояний: Активный, Заблокированный и Приостановленный. Переходы между ними вызываются конкретными событиями:

| Текущее состояние | Событие | Следующее состояние | Ожидаемое поведение |

|------------------|---------|--------------------|--------------------|

| Активный | 5 неудачных входов | Заблокированный | Показать сообщение о блокировке, заблокировать попытки |

| Активный | Администратор приостанавливает | Приостановленный | Показать уведомление о приостановке, завершить сессии |

| Заблокированный | Прошло 15 минут | Активный | Разрешить попытки входа снова |

| Заблокированный | Администратор разблокирует | Активный | Немедленное восстановление доступа |

| Приостановленный | Администратор восстанавливает | Активный | Аккаунт восстановлен, пользователь уведомлён |

| Приостановленный | 5 неудачных входов | Приостановленный | Остаётся приостановленным (без перехода) |

Из этой таблицы выводятся два типа тест-кейсов. Первый тип верифицирует валидные переходы: подтверждает что "Активный + 5 неудачных входов → Заблокированный" работает как задокументировано. Второй тип верифицирует невалидные переходы: подтверждает что Приостановленный аккаунт не может перейти в Заблокированный при попытке входа, и что система корректно обрабатывает этот edge case.

Таблица также раскрывает пробелы. Что происходит если Заблокированный аккаунт получает событие "Администратор приостанавливает"? Требование может не говорить. Диаграммы переходов состояний делают эти пробелы видимыми как пустые ячейки.

Тестирование переходов состояний особенно ценно для рабочих процессов которые джуниор QA склонны недотестировать: что происходит если запустить действие над объектом который уже в "неправильном" состоянии? Что если выполнить шаг 3 до шага 2? Валидные переходы очевидны; невалидные: место где прячутся баги.

Комбинаторное тестирование: когда комбинаций слишком много

У некоторых фич так много входных переменных что полная таблица решений непрактична. Фильтр поиска с 5 опциями каждая с 3–4 возможными значениями даёт тысячи комбинаций. Протестировать все невозможно.

Попарное тестирование (all-pairs testing): практический ответ. Исследования показывают что большинство багов вызвано взаимодействием двух переменных, а не трёх и более. Попарное тестирование гарантирует что каждая пара значений из разных переменных встречается хотя бы в одном тест-кейсе.

Представь тестирование формы бронирования путешествий с такими опциями:

  • Направление: Европа, Азия, Америка (3 значения)
  • Тип поездки: В одну сторону, Туда-обратно (2 значения)
  • Класс: Эконом, Бизнес, Первый (3 значения)
  • Месяц отправления: Лето, Зима (2 значения)

Полное факториальное тестирование: 3 × 2 × 3 × 2 = 36 комбинаций. Попарное тестирование покрывает все пары примерно в 9 тест-кейсах.

Попарные тест-наборы не составляются вручную. Это делают инструменты. PICT (Pairwise Independent Combinatorial Testing): бесплатный инструмент командной строки от Microsoft. AllPairs: ещё один. Задаёшь параметры и значения, запускаешь инструмент, получаешь минимальный тест-набор.

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

Какую технику выбрать: руководство

Техники не взаимоисключающие. Большинство реальных фич используют более одной. Как выбирать.

Разбивка на классы эквивалентности подходит для любого ввода с определённым допустимым диапазоном: поля возраста, диапазоны цен, ограничения длины символов, перечисленные допустимые значения. Используй для организации входных данных до написания отдельных тест-кейсов. Анализ граничных значений сочетается с разбивкой на классы эквивалентности для тех же ситуаций. Всякий раз когда разбивка на классы идентифицирует границу: добавляй BVA тест-кейсы вокруг неё. Всегда. Таблицы решений подходят для фич с несколькими независимыми условиями которые комбинируются давая разные результаты. Если в требованиях логика "если/и/или/если не": строй таблицу решений до написания тест-кейсов. Тестирование переходов состояний подходит для фич с определённым жизненным циклом или рабочим процессом. Если объект (пользователь, заказ, тикет, подписка) может находиться в разных состояниях и переходить между ними: сначала картируй состояния, потом выводи тест-кейсы. Попарное тестирование подходит для конфигурационного тестирования и фич с множеством независимых переменных. Если больше 3–4 переменных и полное покрытие непрактично: используй инструмент для попарного тестирования.

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

Как применять это с понедельника

Не нужно сразу пересматривать весь процесс тестирования. Возьми одну фичу которую тестируешь на этой неделе и примени одну технику.

Тестируешь поле ввода с ограничениями мин/макс: запиши на бумаге классы эквивалентности до открытия приложения. Определи границы. Напиши BVA тест-кейсы. Получишь полный тест поля за пять минут вместо двадцати минут ввода случайных значений.

Тестируешь бизнес-правило с несколькими условиями (логика скидок, контроль доступа, триггеры уведомлений): построй таблицу решений до написания любых тест-кейсов. Каждый столбец становится тест-кейсом. Проверь есть ли неоднозначные комбинации в требованиях и спроси до тестирования.

Тестируешь рабочий процесс с изменениями статусов (управление заказами, жизненный цикл пользователя, маршрутизация тикетов поддержки): нарисуй диаграмму состояний, хотя бы в черновом виде. Перечисли валидные переходы и события которые их вызывают. Потом напиши два тест-кейса на переход: один подтверждающий что работает, один пробующий невалидный переход из похожего состояния.

Цель не в том чтобы применять каждую технику к каждой фиче. Цель: перестать полагаться на память и интуицию для решений которые имеют правильные систематические ответы. Техники превращают вопрос "что тестировать?" из вопроса на который каждый раз отвечаешь по-разному в процесс который можно повторять, защищать и обучать.

→ See also: Как писать тест-кейсы: формат, примеры и частые ошибки | Тестирование на основе рисков: как расставить приоритеты, когда нельзя протестировать всё | Классы эквивалентности и граничные значения: практическое руководство | Тест-кейс vs тест-сценарий: в чём разница и когда использовать каждый