Граничные значения и классы эквивалентности: как проверять на практике
Проверить все возможные значения поля нельзя: их слишком много. Две техники решают эту задачу вместе — классы эквивалентности сокращают перебор, анализ границ говорит, какие значения из оставшихся проверять в первую очередь.
Классы эквивалентности: почему хватает одного значения
Поле «количество товара» принимает числа от 1 до 99. Проверять по очереди 1, 2, 3 и дальше бессмысленно: сервис обрабатывает их одинаково. Все значения, с которыми программа поступает одинаково, образуют класс — и одного представителя класса достаточно.
Классов обычно три: допустимые значения, значения ниже допустимого и выше. К ним добавляются отдельные классы для другого типа данных — пустое поле, строка вместо числа, дробное вместо целого, отсутствие поля вообще. Последний часто забывают, а ведёт он себя иначе, чем пустое значение.
Граница: место, где ломается чаще всего
Ошибки редко живут в середине диапазона. Они живут на краю, потому что там программист выбирал между «больше» и «больше либо равно» — и мог ошибиться на единицу. Эту ошибку так и называют: ошибка на единицу.
ISTQB различает двухточечный и трёхточечный анализ. В двухточечном для каждой границы берут значение самой границы и ближайшего соседнего класса: 0 и 1, затем 99 и 100. Трёхточечный добавляет ближайшее значение внутри допустимого класса: 2 и 98. Он дороже, но обнаруживает больше вариантов ошибки на единицу.
- 0 ниже границы ожидаем отказ
- 1 сама граница ожидаем успех
- 2 внутри диапазона ожидаем успех
- 98 внутри диапазона ожидаем успех
- 99 сама граница ожидаем успех
- 100 выше границы ожидаем отказ
Где границы прячутся в API
Число — самый заметный случай, но далеко не единственный. Границы есть почти у всего, что имеет размер, длину или срок.
- Размер страницы: per_page=0, 1, максимум, максимум + 1. Частая находка — параметр молча игнорируется, и сервис отдаёт всё подряд.
- Длина текста: пустая строка, один символ, максимум по требованиям, максимум + 1. Отдельно — только пробелы: формально строка не пустая.
- Даты периода: начало равно концу, начало позже конца, период длиной в день, будущая дата.
- Деньги: ноль, минимальная сумма, копейка сверх остатка, дробная часть длиннее двух знаков.
- Частота действий: последний разрешённый запрос в окне и следующий за ним — тот, что должен получить отказ.
Как выглядит проверка границы
Смысл в том, чтобы менять ровно одно значение и сравнивать ответы. Если на границе и за границей сервис отвечает одинаково — проверки нет, и это дефект независимо от того, как он выглядит на экране.
GET /products?per_page=50 → 200, в data 50 позиций GET /products?per_page=51 → ожидаем 422 GET /products?per_page=0 → ожидаем 422
Ожидаемый результат для каждой строки берётся из требований, а не из наблюдения. Если в требованиях максимум не задан — это само по себе находка: без верхней границы один запрос может попросить весь каталог разом.
Чего эти техники не ловят
Границы и классы работают с одним полем за раз. Дефекты, которые возникают от сочетания полей — скидка плюс количество плюс остаток на складе, — так не найти: для них нужны другие техники и просто внимание к тому, как значения влияют друг на друга.
Ещё они ничего не говорят о правах доступа и о том, что происходит при повторе одного и того же запроса. Это отдельные виды проверок, и начинать стоит с границ только потому, что они дают больше всего находок за меньшее время.
Как получить набор проверок из требования
Записывай классы до конкретных значений. Для поля “скидка — целое число от 0 до 30 включительно” классы такие: допустимое целое, меньше 0, больше 30, дробное, строка, null, поле отсутствует. Границы применимы только к упорядоченным классам; строка и null — отдельные классы, но не числовые соседи диапазона.
-1 → ниже диапазона, отказ\n0 → нижняя граница, успех\n1 → внутри, успех (только 3-value BVA)\n29 → внутри, успех (только 3-value BVA)\n30 → верхняя граница, успех\n31 → выше диапазона, отказ\n2.5, "10", null, missing → отдельные классы
Один представитель класса достаточен только при обоснованном предположении, что обработка действительно одинакова. Если ноль имеет бизнес-смысл, отрицательные значения проходят другой валидатор, а 30 включает отдельное правило промоакции, это уже разные классы.
Где техника ломается и чем её дополнить
Эквивалентность — модель, а не свойство, которое система обязана соблюдать. Пересматривай классы после каждой находки: дефект часто показывает, что внутри предполагаемого класса есть скрытая граница.
- Сочетания полей: используй decision table или pairwise, если результат зависит от нескольких условий.
- Переходы статусов: используй state transition testing, а не набор независимых значений.
- Права и владение объектом: строй матрицу actor × action × object.
- Время и даты: уточняй timezone, включительность границы и переход летнего времени.
- Деньги: уточняй единицу хранения, округление, валюту и допустимую точность до выбора чисел.
Частые вопросы о границах
Всегда ли нужно брать три значения у границы?
Нет. Двухточечный BVA берёт границу и ближайшее значение соседнего класса; трёхточечный добавляет значение с другой стороны. Выбор зависит от риска и нужной силы покрытия.
Пустая строка и отсутствующее поле — один класс?
Не обязательно. Пустая строка передана явно, null может означать отсутствие значения, а missing запускает default или проверку required. Пока контракт не доказывает одинаковую обработку, проверяй их отдельно.
Можно ли применять границы к enum?
У enum нет естественного числового порядка, поэтому BVA обычно неприменим. Выдели допустимые и недопустимые классы, регистр, unknown value и отсутствие поля.
Техника запоминается не с первого прочтения, а после того, как одну и ту же границу проверишь в фильтре, в размере страницы и в суточном лимите — и увидишь, что это одна и та же проверка.