Тест-дизайн: граничные значения
Boundary Value Analysis (BVA) — техника тест-дизайна, основанная на том, что большинство ошибок возникают на границах допустимых диапазонов (минимум, максимум, граничные значения ±1).
Зачем нужно
Большинство off-by-one ошибок скрываются именно на границах: > вместо >=, length - 1 вместо length. Тестирование серединных значений не поймает их. Граничные значения дают максимальную плотность нахождения багов при минимальном количестве тестов.
Где используется
- Валидация числовых диапазонов (возраст, цена, количество)
- Строковые ограничения (минимальная/максимальная длина)
- Пагинация (первая страница, последняя, граничный offset)
- Массивы (пустой, один элемент, граничный индекс)
Основной контент
Принцип граничных значений
Для диапазона [min, max] тестируй: min-1, min, min+1, типичное значение, max-1, max, max+1
Пример: валидация возраста (18–100)
function isValidAge(age) {
return age >= 18 && age <= 100;
}
describe('isValidAge — граничные значения', () => {
// Ниже минимума
test('17 — невалиден (min-1)', () => expect(isValidAge(17)).toBe(false));
// Граница минимума
test('18 — валиден (min)', () => expect(isValidAge(18)).toBe(true));
test('19 — валиден (min+1)', () => expect(isValidAge(19)).toBe(true));
// Типичное значение
test('50 — валиден (середина)', () => expect(isValidAge(50)).toBe(true));
// Граница максимума
test('99 — валиден (max-1)', () => expect(isValidAge(99)).toBe(true));
test('100 — валиден (max)', () => expect(isValidAge(100)).toBe(true));
test('101 — невалиден (max+1)', () => expect(isValidAge(101)).toBe(false));
});
Пример: длина пароля (8–32 символа)
function validatePasswordLength(password: string): boolean {
return password.length >= 8 && password.length <= 32;
}
test.each([
['', false], // пустая строка
['abc', false], // 3 символа
['1234567', false], // 7 символов (min-1)
['12345678', true], // 8 символов (min)
['123456789', true], // 9 символов (min+1)
['a'.repeat(32), true], // 32 символа (max)
['a'.repeat(33), false], // 33 символа (max+1)
])('validatePasswordLength("%s") → %s', (pwd, expected) => {
expect(validatePasswordLength(pwd)).toBe(expected);
});
Пример: пагинация
function getPage(items: string, page: number, perPage: number = 10) {
if (page < 1) throw new Error('page must be >= 1');
const start = (page - 1) * perPage;
return items.slice(start, start + perPage);
}
const items = Array.from({ length: 25 }, (_, i) => `item_${i + 1}`);
test('page 0 — ошибка (min-1)', () => {
expect( => getPage(items, 0)).toThrow;
});
test('page 1 — первая страница (min)', () => {
expect(getPage(items, 1)).toHaveLength(10);
expect(getPage(items, 1)[0]).toBe('item_1');
});
test('page 3 — последняя неполная страница', () => {
expect(getPage(items, 3)).toHaveLength(5); // 25 - 20 = 5
});
test('page 4 — за пределами данных (max+1)', () => {
expect(getPage(items, 4)).toHaveLength(0);
});
Матрица граничных значений
Диапазон | Тест-кейсы
----------|------------------------------------------
[a, b] | a-1, a, a+1, середина, b-1, b, b+1
[0, ∞) | -1, 0, 1, типичное
строка | '', min-1 символ, min, min+1, max, max+1
массив | , [один], [два], [max-1], [max]
Частые ошибки
- Тест только середины диапазона —
expect(validate(50)).toBe(true)не ловит off-by-one на границах - Нет теста на min и max одновременно — оба граничных значения должны быть протестированы, не только один
- Игнорирование специальных значений —
null,undefined,NaN,Infinity— тоже граничные случаи для числовых функций
Связанные темы
- _MOC Тестирование
- Тест-дизайн -- эквивалентные классы
- Тестирование ошибок и edge cases
- Unit тесты
- TDD