Тестирование каждого возможного входного значения невозможно. Простое поле пароля, принимающее 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

  1. Сокращает Тест-Кейсы: С тысяч до десятков
  2. Систематическое Покрытие: Обеспечивает тестирование всех классов входов
  3. Обнаружение Дефектов: Один тест на класс обнаруживает сбои всего класса
  4. Поддерживаемость: Легко обновлять при изменении требований
  5. Эффективность: Максимизирует покрытие с минимальными усилиями

Заключение

Разделение на Классы Эквивалентности — это фундаментальная техника проектирования тестов, которая:

  • Делит входные данные на логические группы
  • Тестирует одно представительное значение на группу
  • Резко сокращает тест-кейсы, сохраняя покрытие
  • Работает лучше всего в сочетании с Анализом Граничных Значений

Помните: Если вы тестируете одно значение из класса и оно работает, весь класс должен работать. Этот принцип позволяет вам тестировать умнее, а не усерднее.