Граничные значения
Тот же возраст от 18 до 65. Классы эквивалентности дают три теста, а анализ границ - четыре: 17, 18, 65, 66. И именно эти четыре ловят самую частую ошибку в коде: знак сравнения перепутан, стоит «больше» вместо «больше или равно».
Стержень: ошибки живут на переходах между классами, поэтому проверяют не середину, а точки прямо по обе стороны границы.
// Формулировки: «почему баги живут на границах?», «сколько значений брать на границу?», «границы есть только у чисел?».
Почему именно границы
Причина в том, как пишется код. Граница - это место, где стоит сравнение, и ровно там делают опечатку: строгое вместо нестрогого, «меньше» вместо «меньше или равно», единица туда-сюда в индексе. Значение из середины класса такую опечатку не покажет никогда, потому что для него обе версии сравнения дают одинаковый ответ.
Считаем аккуратно. Двухточечный анализ берёт по два значения на границу: последнее снаружи и первое внутри. Для диапазона 18-65 это 17, 18 и 65, 66 - четыре теста. Трёхточечный добавляет соседа изнутри: 17, 18, 19 и 64, 65, 66 - шесть тестов. Второй надёжнее, первый дешевле; на важных полях берут трёхточечный, на остальных двухточечный.
// Классы и границы применяют вместе, а не вместо друг друга. Классы говорят, СКОЛЬКО групп проверять, границы - КАКИЕ значения из них брать. Начинают всегда с классов: иначе легко проверить границы диапазона и пропустить целую группу вроде «поле не заполнено».
- двухточечный анализ
- по два значения на границу: последнее снаружи, первое внутри
- трёхточечный анализ
- добавляет соседа изнутри, надёжнее и дороже
Границы есть почти у всего
У длины строки границы там же, где ограничения: ноль символов, один, максимум, максимум плюс один. Причём максимум надо считать в тех же единицах, что и система: если ограничение в символах, а проверка написана по байтам, кириллица и эмодзи сломают её задолго до заявленного предела.
У коллекций границы такие: пустая, из одного элемента, полная по лимиту, на один больше лимита. Пустая коллекция - отдельный чемпион по багам: деление на её размер, обращение к первому элементу, «средний по пустому списку».
// У чисел с плавающей точкой границы коварнее: там есть значения, которые не представимы точно, и сравнение на равенство работает не так, как ожидается. У денег поэтому берут целые копейки или специальный десятичный тип, а не дробное число - и это тоже проверяют тестом на границе.
- границы коллекции
- пустая, из одного, полная, на один больше лимита
Время и постраничная выдача
Время - источник границ, которые вспоминают в последнюю очередь. Полночь, конец месяца, тридцать первое декабря, двадцать девятое февраля, переход на летнее время, разные часовые пояса у пользователя и сервера. Классика: отчёт «за сегодня» у пользователя во Владивостоке считается по серверной дате и показывает вчерашние данные.
У постраничной выдачи границы такие: первая страница, последняя, страница за последней, размер страницы ноль, размер больше максимума, ровно кратное количество записей. Последнее особенно вредное: когда записей ровно на целое число страниц, часто появляется лишняя пустая страница или пропадает последняя запись.
// И граница, о которой забывают почти всегда: пустой результат. Ноль записей вообще - не то же самое, что «первая страница пуста», и обрабатывается это разным кодом.
- границы времени
- полночь, конец месяца, високосный день, смена пояса
Как отвечать: «Почему баги живут на границах и как их тестировать?»
Потому что граница - это место, где в коде стоит сравнение, и именно там делают опечатку: строгое вместо нестрогого, единица в ту или другую сторону. Значение из середины класса такую ошибку не покажет: для него оба варианта сравнения дают одинаковый результат. Беру двухточечный анализ - последнее значение снаружи и первое внутри, для диапазона от 18 до 65 это 17, 18, 65 и 66. На важных полях беру трёхточечный, добавляя соседа изнутри: получается шесть значений. Работает это не только с числами: у строки границы по длине, у коллекции - пустая, один элемент, полная и на один больше, у постраничной выдачи - первая страница, последняя, за последней и ровно кратное количество записей. И обязательно применяю границы вместе с классами эквивалентности, а не вместо: классы говорят, сколько групп проверять, границы - какие значения из них брать.
Почему это сильный ответ: названа причина через устройство кода, приведена конкретная схема с числами, показано, что техника не только про числа, и обозначена связь с классами.
На чём валят
- −Проверять только середину диапазона. Перепутанный знак сравнения таким тестом не ловится в принципе.
- −Брать границы и забыть классы. Диапазон проверен, а группа «поле не заполнено» пропущена целиком.
- −Считать длину строки в байтах, когда ограничение в символах. Кириллица и эмодзи упрутся в предел раньше заявленного.
- −Пропустить пустую коллекцию и нулевой результат. Это отдельная ветка кода, и падает она чаще прочих.
- −Игнорировать часовые пояса и конец месяца. Отчёт «за сегодня» у пользователя в другом поясе покажет вчерашнее.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 13, остальные разбираются в тренажёре.
- Поле принимает целые от 1 до 100. Какие значения берёт анализ границ?A)Достаточно самих границ 1 и 100; соседние с ними значения проверять не требуетсяB)Края и их соседей: 0, 1, 2 и 99, 100, 101 — по обе стороны каждой границыC)Значения 50 и 51 из середины диапазона, где поведение системы наиболее стабильноD)Случайные 10 значений внутри диапазона 1–100 для равномерного покрытия поля
показать ответ и разбор
+B)Края и их соседей: 0, 1, 2 и 99, 100, 101 — по обе стороны каждой границы// разбор: Анализ границ проверяет не только сами края (1 и 100), но и ближайших соседей по обе стороны: значение чуть ниже границы (недопустимое), сама граница (допустимая), значение сразу за ней. Для диапазона 1–100 это 0/1/2 у нижнего края и 99/100/101 у верхнего — трёхточечный (3-value) вариант. Так проверяется, что условие «принять» срабатывает ровно на границе и не срабатывает на шаг за ней. Значение 0 и 101 должны быть отвергнуты, 1 и 100 — приняты.
- Чем двухточечный (2-value) граничный анализ отличается от трёхточечного (3-value)?A)2-value проверяет числовые границы, а 3-value — строковые; выбор зависит от типа поляB)2-value применяют в ручном тестировании, а 3-value — в автоматизированномC)2-value берёт границу и одного соседа; 3-value — ещё и соседа с другой стороныD)3-value берёт три случайные точки диапазона, а 2-value — две, ради экономии времени
показать ответ и разбор
+C)2-value берёт границу и одного соседа; 3-value — ещё и соседа с другой стороны// разбор: Оба варианта проверяют края, но с разной плотностью. Двухточечный берёт для каждой границы два значения: саму границу и ближайшего соседа (например, 100 и 101). Трёхточечный добавляет соседа и с другой стороны (99, 100, 101), явно проверяя и «внутри», и «на границе», и «за границей». 3-value надёжнее ловит ошибки в условии сравнения ценой большего числа тестов. Выбор — компромисс риск/стоимость: для критичной логики берут 3-value, для менее рисковой достаточно 2-value.
- Какую распространённую ошибку кода прицельно ловит анализ граничных значений?A)Ошибку деления на ноль, которая возникает при пустом вводе в числовое полеB)Утечку памяти, которая накапливается при длительной обработке крайних значенийC)Ошибку кодировки символов при вводе значений на верхней границе строкового поляD)Off-by-one: ошибку на единицу в условии (≤ вместо <), из-за которой крайнее значение принимается/отвергается неверно
показать ответ и разбор
+D)Off-by-one: ошибку на единицу в условии (≤ вместо <), из-за которой крайнее значение принимается/отвергается неверно// разбор: Off-by-one — классика: разработчик написал <= вместо <, или обработал массив на один элемент дальше/ближе. Такой дефект проявляется ровно на границе и её соседе, а в середине диапазона незаметен. Тест на самом граничном значении (принимается ли 100 при лимите «до 100»?) и на соседе (отвергается ли 101?) вскрывает несовпадение фактического поведения с ожидаемым. Именно нацеленность на эти точки делает граничный анализ таким результативным на единицу вложенных тестов.
- Есть ли границы у нечисловых полей — например, у строки или списка?A)Да: у длины строки и размера коллекции — пустое, 1 элемент, максимально допустимое и на один сверх лимитаB)Нет: границы бывают у числовых диапазонов, а у строк и списков их практически нетC)Есть, но у списков; у строк же проверяют лишь корректность каждого отдельного символаD)Есть, но границей строки считается её алфавитный порядок, а не длина
показать ответ и разбор
+A)Да: у длины строки и размера коллекции — пустое, 1 элемент, максимально допустимое и на один сверх лимита// разбор: Граничный анализ применим не только к числовым значениям, но и к размерным характеристикам. У строки границы — это длина: пустая строка (0), строка из одного символа, строка максимально допустимой длины и на символ длиннее лимита. У списка/массива — число элементов: пустой, один элемент, полный по лимиту, переполнение. Эти края часто ломают код (пустой ввод, переполнение буфера, обработка последнего элемента). Поэтому «граница» — это край любого упорядоченного или размерного параметра, а не только число в поле.
- Как открытость/закрытость интервала (< или ≤) влияет на граничный тест?A)Открытость интервала меняет оформление тест-кейса, но не сам набор проверяемых значенийB)Она определяет, валидно ли само граничное значение; тест на границе это и проверяетC)При открытом интервале граничные значения не проверяют, потому что граница недостижимаD)Открытость интервала важна для чисел с плавающей точкой, а для целых полей роли не играет
показать ответ и разбор
+B)Она определяет, валидно ли само граничное значение; тест на границе это и проверяет// разбор: Спецификация «до 100» неоднозначна: включает 100 или нет? Это разница между < 100 (100 недопустимо) и ≤ 100 (100 допустимо). Именно поэтому тест ставят на само граничное значение: он проверяет, что реализация трактует границу так же, как требование. Если по спецификации 100 должно приниматься, а код написал < 100, тест на 100 покажет расхождение. Соседи (99, 101) подтверждают, что условие срабатывает не раньше и не позже нужного. Уточнение открытости интервала — обязательный шаг перед проектированием граничных тестов.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.