Тестирование каждого возможного входного значения невозможно. Простое поле пароля, принимающее 8-32 символа, имеет миллиарды потенциальных входов. Как тестировать всесторонне, не тратя годы на написание тест-кейсов?
Встречайте Разделение на Классы Эквивалентности (EP) — фундаментальную технику проектирования тест-кейсов и подход тестирования черного ящика, которая делит входные данные на логические группы (классы), где все значения в классе должны вести себя одинаково. Тестируя одно представительное значение из каждого класса, вы достигаете широкого покрытия с минимальным количеством тест-кейсов.
Основная Концепция
Принцип Разделения на Классы Эквивалентности: Если одно значение из класса работает правильно, все значения в этом классе должны работать правильно. И наоборот, если одно значение не работает, все значения в этом классе должны не работать.
Вместо тестирования:
Ввод возраста: 5, 10, 15, 18, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80...
Вы тестируете:
Классы возраста:
- Невалидный (слишком молод): Тестовое значение = 10
- Валидный (взрослый): Тестовое значение = 40
- Невалидный (слишком стар): Тестовое значение = 75
Три тест-кейса вместо десятков, с такой же или лучшей обнаруживаемостью дефектов.
Как Применять Разделение на Классы Эквивалентности
Шаг 1: Определить Входные Условия
Перечислите все входы и их требования:
Пример: Система Бронирования Билетов
Вход: Количество билетов
Требование: Должно быть между 1 и 10 (включительно)
Шаг 2: Определить Классы Эквивалентности
Разделите входное пространство на валидные и невалидные классы:
Тип Класса | Диапазон | Описание |
---|---|---|
Невалидный | < 1 | Слишком мало билетов |
Валидный | 1-10 | Приемлемый диапазон |
Невалидный | > 10 | Слишком много билетов |
Шаг 3: Выбрать Тестовые Значения
Выберите одно представительное значение из каждого класса:
Класс | Тестовое Значение | Ожидаемый Результат |
---|---|---|
Невалидный (< 1) | 0 | Ошибка: “Требуется минимум 1 билет” |
Валидный (1-10) | 5 | Бронирование успешно |
Невалидный (> 10) | 15 | Ошибка: “Максимум 10 билетов разрешено” |
Результат: 3 тест-кейса покрывают все сценарии
Валидные vs Невалидные Классы
Каждый вход обычно имеет:
Валидные Классы
- Входы, которые система должна принять
- Ожидается, что они приведут к успешным результатам
- Часто есть один валидный класс, но сложные входы могут иметь несколько
Невалидные Классы
- Входы, которые система должна отклонить
- Ожидается, что они приведут к ошибкам
- Обычно несколько невалидных классов (ниже диапазона, выше диапазона, неправильный тип, null и т.д.)
Пример: Поле Email
Валидные Классы:
- ✅ Стандартный email: "user@example.com"
- ✅ Email с числами: "user123@test.com"
- ✅ Email с поддоменом: "user@mail.example.com"
Невалидные Классы:
- ❌ Отсутствует @: "userexample.com"
- ❌ Отсутствует домен: "user@"
- ❌ Отсутствует локальная часть: "@example.com"
- ❌ Несколько @: "user@@example.com"
- ❌ Пустая строка: ""
- ❌ Специальные символы: "user name@example.com"
- ❌ Слишком длинный: [email > 254 символов]
Стратегия Тестирования: Выберите 1-2 значения из валидных классов, 1 значение из каждого невалидного класса.
Пример Тест-Кейса EP
**Функция**: Регистрация Пользователя - Поле Возраста
**Требование**: Возраст должен быть 18-65 лет
**Классы Эквивалентности:**
| ID Класса | Тип Класса | Диапазон | Тестовое Значение | Ожидаемый Результат |
|----------|-----------|---------|------------------|-------------------|
| EP1 | Невалидный | Возраст < 18 | 15 | Ошибка: "Вам должно быть 18 или больше" |
| EP2 | Валидный | 18 ≤ Возраст ≤ 65 | 30 | Регистрация успешна |
| EP3 | Невалидный | Возраст > 65 | 70 | Ошибка: "Превышен лимит возраста" |
| EP4 | Невалидный | Нечисловой | "abc" | Ошибка: "Возраст должен быть числом" |
| EP5 | Невалидный | Отрицательный | -5 | Ошибка: "Возраст должен быть положительным" |
| EP6 | Невалидный | Десятичный | 25.5 | Ошибка: "Возраст должен быть целым числом" |
| EP7 | Невалидный | Пустой | "" | Ошибка: "Требуется возраст" |
**Всего Тест-Кейсов**: 7 (вместо тестирования всех возрастов 0-999)
Комбинирование EP с Анализом Граничных Значений (BVA)
Разделение на Классы Эквивалентности и Анализ Граничных Значений работают идеально вместе:
Только EP:
Валидный класс (18-65): Тестовое значение = 30
EP + BVA:
Валидный класс (18-65):
- Тестовое значение = 18 (нижняя граница)
- Тестовое значение = 30 (средний диапазон)
- Тестовое значение = 65 (верхняя граница)
Пример: Уровни Скидок
Требование: Скидки при покупке на основе суммы
- $0-$99: Без скидки
- $100-$499: 10% скидка
- $500-$999: 15% скидка
- $1000+: 20% скидка
Классы EP:
Класс | Диапазон | Тестовое Значение EP | Тестовые Значения EP+BVA |
---|---|---|---|
Уровень 1 | $0-$99 | $50 | $0, $50, $99 |
Уровень 2 | $100-$499 | $300 | $100, $300, $499 |
Уровень 3 | $500-$999 | $750 | $500, $750, $999 |
Уровень 4 | $1000+ | $1500 | $1000, $1500, $5000 |
Только EP: 4 тест-кейса EP + BVA: 12 тест-кейсов (более тщательно)
Примеры из Реального Мира
Пример 1: Валидация Пароля
Требования:
- Длина: 8-20 символов
- Должен содержать: заглавную букву, строчную букву, цифру, специальный символ
- Не может содержать: имя пользователя, общие слова
Классы Эквивалентности:
Валидный Класс:
EP-V1: Валидный пароль, удовлетворяющий всем критериям
Тест: "MyP@ssw0rd"
Невалидные Классы:
EP-I1: Слишком короткий (< 8 символов)
Тест: "Pass1!"
EP-I2: Слишком длинный (> 20 символов)
Тест: "MyVeryLongP@ssw0rd123456"
EP-I3: Отсутствует заглавная буква
Тест: "myp@ssw0rd1"
EP-I4: Отсутствует строчная буква
Тест: "MYP@SSW0RD1"
EP-I5: Отсутствует цифра
Тест: "MyP@ssword"
EP-I6: Отсутствует специальный символ
Тест: "MyPassword1"
EP-I7: Содержит имя пользователя
Тест: "JohnP@ssw0rd" (если имя пользователя "john")
EP-I8: Общее слово
Тест: "Password123!"
EP-I9: Пустой
Тест: ""
Автоматизация Тестов:
import pytest
@pytest.mark.parametrize("password,expected,reason", [
# Валидный класс
("MyP@ssw0rd", True, "Валидный пароль"),
# Невалидные классы
("Pass1!", False, "Слишком короткий"),
("MyVeryLongP@ssw0rd123456", False, "Слишком длинный"),
("myp@ssw0rd1", False, "Отсутствует заглавная"),
("MYP@SSW0RD1", False, "Отсутствует строчная"),
("MyP@ssword", False, "Отсутствует цифра"),
("MyPassword1", False, "Отсутствует спец. символ"),
("Password123!", False, "Общее слово"),
("", False, "Пустой"),
])
def test_password_validation(password, expected, reason):
result = validate_password(password)
assert result == expected, f"Не прошло: {reason}"
Типичные Ошибки
❌ Ошибка 1: Пересекающиеся Классы
Плохо:
- Класс 1: Возраст 0-50
- Класс 2: Возраст 50-100
Проблема: Возраст 50 в обоих классах!
Хорошо:
- Класс 1: Возраст 0-49
- Класс 2: Возраст 50-100
❌ Ошибка 2: Отсутствующие Невалидные Классы
Плохо (только тестирование валидных):
- Класс: Валидный возраст (18-65)
- Тест: 30
Хорошо (тестирование валидных + невалидных):
- Класс 1: Слишком молод (< 18)
- Класс 2: Валидный (18-65)
- Класс 3: Слишком стар (> 65)
Преимущества EP
- Сокращает Тест-Кейсы: С тысяч до десятков
- Систематическое Покрытие: Обеспечивает тестирование всех классов входов
- Обнаружение Дефектов: Один тест на класс обнаруживает сбои всего класса
- Поддерживаемость: Легко обновлять при изменении требований
- Эффективность: Максимизирует покрытие с минимальными усилиями
Заключение
Разделение на Классы Эквивалентности — это фундаментальная техника проектирования тестов, которая:
- Делит входные данные на логические группы
- Тестирует одно представительное значение на группу
- Резко сокращает тест-кейсы, сохраняя покрытие
- Работает лучше всего в сочетании с Анализом Граничных Значений
Помните: Если вы тестируете одно значение из класса и оно работает, весь класс должен работать. Этот принцип позволяет вам тестировать умнее, а не усерднее.