Модуль 6.1. Тест-дизайн для аналитика
Темы: классы эквивалентности, граничные значения, негативные сценарии. Артефакты: тест-сценарии к требованиям. Практика: 10 тестов на одну фичу.
Системный аналитик не «тестировщик», но именно он превращает требования в проверяемые сценарии. От качества вашего тест-дизайна зависит, поймает ли команда ошибки до релиза, а не у клиента. В этом модуле вы получите:
- рабочие шаблоны классов эквивалентности и граничных значений для чисел, строк, дат, массивов, статусов;
- чек-лист негативных сценариев (валидация, авторизация, идемпотентность, конкуренция, лимиты);
- артефакт «Тест-сценарии к требованиям» с трассировкой в RTM;
- пошаговую методику: как из AC/контрактов сделать короткий, но полный набор тестов.
Роль аналитика в тест-дизайне (как сотруднику — к исполнению)
- Убедиться, что у каждой фичи есть AC (acceptance criteria) и измеримые NFR.
- Выписать входные параметры и ограничения (тип/диапазон/формат/обязательность).
- Сформировать классы эквивалентности (валидные/невалидные) по каждому параметру.
- Выделить границы (min/max/переходные состояния/лимиты) — и проверить их «по обе стороны».
- Дополнить негативами: авторизация, идемпотентность, конкуренция, сбои зависимостей, таймауты, rate-limit.
- Сопоставить тесты требованиям (RTM): «Req-7.2 → TC-15, TC-16…».
- Определить оракулы (что считать правильным результатом): схема ответа, SQL-инварианты, события, записи в аудит-логе, метрики.
Классы эквивалентности (Equivalence Partitioning)
Идея
Если система одинаково обрабатывает большие группы значений, тестируем по представителю каждой группы (класса), а не все значения.
Шаблон для каждого параметра
Параметр: <имя>, тип: <число/строка/дата/enum/массив> Ограничения: <мин/макс/регекс/обязательность/зависимости> Классы эквивалентности: Валидные (V1, V2, …): <описание + пример значения> Невалидные (I1, I2, …): <описание + пример значения>
Типовые классы (повесьте на стену)
-
Числа (money, qty):
V1: внутри (min..max); V2: ровно min; V3: ровно max;
I1: < min; I2: > max; I3: не число/NaN; I4: больше допустимой точности. -
Строки (id, код, email):
V1: корректный формат/длина;
I1: пусто при required; I2: длина < min; I3: длина > max; I4: недопустимые символы; I5: регистр/локаль (кириллица vs латиница) если запрещено. -
Даты/время:
V1: в допустимом интервале, с TZ;
I1: без TZ; I2: за пределами окна; I3: «перевёрнутые» from/to; I4: 29.02 без високосности. -
Enum/статус:
V1: один из кодов справочника (активных на дату);
I1: устаревший/неизвестный код. -
Ссылочная целостность:
I: foreign key не существует/принадлежит другому тентанту. -
Массивы:
I1: пусто, если minItems>0; I2: > maxItems; I3: дубликаты при uniqueItems.
Граничные значения (Boundary Value Analysis)
Идея
Ошибки чаще живут на границе (off-by-one). Проверяйте минимум 5 точек: мин-1, мин, мин+1, макс-1, макс, макс+1 (если применимо).
Денежные суммы (пример)
-
Диапазон: 0.01 ≤ amount ≤ 100 000.00; 2 знака после запятой.
Тест-точки: 0.00 (−), 0.01 (✓), 0.02 (✓), 99 999.99 (✓), 100 000.00 (✓), 100 000.01 (−), 0.001 (−).
Даты/окна
-
Разрешено: createdAt ∈ [T0; T0+90д].
Проверить: T0−1 сек (−), T0 (✓), T0+1 сек (✓), T0+90д (✓), T0+90д+1 сек (−).
Учесть TZ и переходы на летнее/зимнее время.
Строки/длины
-
idempotency-key: 1..128 ASCII.
Точки: 0 (−), 1 (✓), 2…127 (✓), 128 (✓), 129 (−), не-ASCII (−).
Негативные сценарии (Negative/Fail-paths)
Категории
- Валидация: формат, диапазон, обязательность, несовместимые поля.
- Авторизация/аутентификация: нет токена, просрочен, нет scope/ролей, RLS (чужой tenant).
- Идемпотентность/повторы: повторный POST с тем же ключом, повтор после таймаута.
- Конкуренция: гонки обновлений, версии (If-Match/ETag), «двойное списание».
- Зависимости: таймаут внешнего PSP, недоступна БД, DLQ/очередь переполнена.
- Лимиты: rate-limit, квоты, размер тела, глубина пагинации.
- Безопасность: инъекции (SQL/JSON), XSS в отображаемых полях (для web), directory traversal (для загрузок).
- Наблюдаемость: отсутствие trace_id/аудит-записи при ошибке.
Что проверять в ответах
- Код/карта ошибок (machine-readable code, message, details[]).
- Локализация (если обещана).
- Корректные заголовки (Retry-After, WWW-Authenticate).
- Отсутствие чувствительных данных в ошибке.
Артефакт: «Тест-сценарии к требованиям» (шаблон)
# Test Scenarios for Feature <Название> vX.Y.Z
Owner: <Команда> | SA: <ФИО> | Связи: RTM-42..47, OpenAPI /events schemas
## 1. Область
Фича: <описание кратко>. Источник правды: <таблицы/сервисы>. Затронутые контракты: <эндпоинты/топики>.
## 2. Параметры и классы эквивалентности
Параметр: amount (decimal(18,2)) — min=0.01, max=100000.00
V1: (0.01..100000.00); V2: =min; V3: =max
I1: <0.01; I2: >100000.00; I3: scale>2; I4: not number
...
## 3. Границы
amount: 0.00(−), 0.01(✓), 0.02(✓), 99999.99(✓), 100000.00(✓), 100000.01(−)
idempotency-key: 0(−), 1(✓), 128(✓), 129(−)
## 4. Негативы
Auth: no token → 401; no scope → 403; foreign tenant → 403/RLS.
Dependencies: PSP timeout → 202 Accepted + задача в очередь.
Rate-limit: >100 req/min → 429 + Retry-After.
## 5. Тест-кейсы (таблица)
| TC | Тип | Заголовок | Предусловия | Данные/Ввод | Шаги | Оракул/Ожидаемо |
|----|-----|-----------|-------------|-------------|------|-----------------|
| TC-01 | EC+BV | amount=0.01 min | токен валиден | {amount:0.01,...} | POST /payments | 201, запись в БД, событие PaymentAuthorized |
| ... | ... | ... | ... | ... | ... | ... |
## 6. Оркестрация проверки
- API ответ; запись в БД (SQL); событие в шине (Avro/Registry); аудит-лог; метрики (p95, error%).
Оркеструйте «оракул»: что и где проверяем — ответ, БД, событие, логи, метрики.
Практика: 10 тестов на одну фичу
Кейс: POST /v1/payments — «Создать платёж» (идемпотентная операция).
Требования (фрагмент):
- amount decimal(18,2), 0.01…100 000.00; currency — ^[A-Z]{3}$ (поддержка: RUB, USD, EUR);
- orderId — UUID существующего заказа в том же tenant;
- Idempotency-Key (заголовок) 1..128 ASCII; повтор с тем же ключом → тот же paymentId;
- При недоступности PSP — 202 (асинхронная обработка), задача в очередь, публикуется событие по завершении;
- Auth: OAuth2 scope payments:write.
Параметры и классы (выжимка)
- amount: V1 внутри диапазона; V2=min; V3=max; I1<min; I2>max; I3 scale>2; I4 NaN.
- currency: V1=RUB|USD|EUR; I1=неизвестный ABC; I2=строка ≠3 символов; I3=lowercase.
- Idempotency-Key: V1 длина 1..128 ASCII; I1 пусто; I2 длина>128; I3 non-ASCII.
- orderId: V1 реальный UUID своего tenant; I1 не UUID; I2 UUID чужого tenant; I3 нет такого заказа.
- Auth: V1 валидный токен со scope; I1 нет токена; I2 нет scope.
Набор из 10 тестов (EC/BV/NEG)
|
TC |
Тип |
Название |
Предусловия |
Ввод (ключевые поля) |
Шаги |
Ожидаемо (оракулы) |
|---|---|---|---|---|---|---|
|
TC-01 |
EC+BV |
Минимальная сумма |
Заказ O1 (tenant=A), токен со scope |
amount=0.01, currency=RUB, orderId=O1, IdemKey=K1 |
POST /payments |
201, тело с paymentId, в БД payment.amount=0.01, событие PaymentAuthorized |
|
TC-02 |
EC+BV |
Максимальная сумма |
как выше |
amount=100000.00, currency=USD |
POST |
201, запись, событие |
|
TC-03 |
NEG-VAL |
Сумма ниже минимума |
— |
amount=0.00, currency=RUB |
POST |
422 code=AMOUNT_BELOW_MIN; нет записи/события |
|
TC-04 |
NEG-VAL |
Сумма с тремя знаками |
— |
amount=10.001 |
POST |
422 code=AMOUNT_SCALE_INVALID |
|
TC-05 |
NEG-REF |
Неверный orderId |
— |
orderId="not-uuid" |
POST |
400 code=ORDER_ID_FORMAT; аудит-лог причины |
|
TC-06 |
NEG-RLS |
Чужой tenant |
Заказ O2 (tenant=B) |
orderId=O2 (клиент из A) |
POST |
403 code=FORBIDDEN; запись отсутствует |
|
TC-07 |
NEG-AUTH |
Нет токена |
— |
валидные данные |
POST |
401 WWW-Authenticate; без побочек |
|
TC-08 |
IDEM |
Идемпотентность: повтор |
Первый запрос вернул 201, paymentId=P1 |
Повтор того же запроса, IdemKey=K1 |
POST×2 |
Второй ответ 200/201 с тем же paymentId=P1; дублей в БД нет |
|
TC-09 |
NEG-DEPS |
PSP timeout → деградация |
Смоделировать недоступный PSP |
валидные данные |
POST |
202 Accepted; задача в очередь (queue_depth+1); нет записи «captured», будет асинхронно |
|
TC-10 |
NEG-RATE |
Рейт-лимит |
100 запросов/мин достигнут |
следующий POST |
POST |
429 + Retry-After; лог метрики rate_limit_hits_total++ |
Подсказка: если нужно расширить до 12–15, добавьте: currency=abc (неподдерживаемый код), IdemKey длина 129, «повтор после таймаута клиента» (идемпотентность), конкуренция (два запроса с разными ключами на один заказ).
SQL/события/логи как оракулы (фрагменты)
-
SQL:
SELECT COUNT(*) FROM payment WHERE idempotency_key='K1' → 1;
SELECT currency FROM payment WHERE payment_id=P1 = 'RUB'. - Событие: payment.authorized.v1 (envelope без PII), схема из Registry.
- Логи: запись уровня info с trace_id, orderId, без PII; при ошибках — error.code.
Тонкости и риски (анти-паттерны)
|
Риск |
Как проявляется |
Как предотвратить |
|---|---|---|
|
Проверяем «счастливую тропу» и пару ошибок |
Баги на границах вылезают в проде |
Всегда делайте границы 5-точек и по одному представителю каждого класса |
|
Не тестируем негативы с внешними зависимостями |
Всплески 5xx, ретрай-шторм |
Тесты деградаций: таймаут PSP, отключение БД/кэша, DLQ |
|
Путаем источник правды |
Расхождения API↔БД↔события |
Явные оракулы: где что проверяем; инварианты SQL |
|
Идемпотентность не покрыта |
Дубли платежей |
Тест «повтор тем же ключом», «повтор после таймаута» |
|
Неправильная локаль/TZ |
«Слетевшие» окна дат |
Тесты с TZ и переходами DST; всё в UTC внутр. |
|
Проглатываем ошибки |
Нет диагностики |
В AC: структура ошибок, обязательные поля в логах/трейсах |
Процесс: как быстро собрать качественный набор
- Пройдитесь по OpenAPI/схемам и AC → выписать параметры/ограничения.
- Для каждого — классы эквивалентности (2–3 валидных, 2–4 невалидных).
- Для числовых/дат — добавить границы (5 точек).
- Выбрать минимальный набор: 6–10 тестов, покрывающих все классы и ключевые границы.
- Добавить 2–3 негативных по зависимостям/идемпотентности.
- Прописать оракулы (ответ, БД, событие, логи).
- Сопоставить с RTM; отметить, какие SLO/метрики наблюдаем в тестах.
FAQ — Вопрос/Ответ
В: Чем классы эквивалентности отличаются от граничных значений?
О: Классы — про представителей групп значений (внутри/вне правил). Границы — про краевые точки, где чаще всего живут дефекты.
В: Сколько тестов достаточно?
О: Для одной фичи обычно 8–15: представители всех классов, ключевые границы, 2–3 негативных с зависимостями. Дальше — риск-базированный приоритет.
В: Нужно ли проверять БД и события, если API вернул 200?
О: Да. API может «сказать» одно, а источник правды — другое. Оракулы должны быть сквозными.
В: Как тестировать идемпотентность?
О: Повтор тем же Idempotency-Key (тот же ответ), повтор после таймаута клиента, конкурентные повторы — и убедиться, что в БД один объект.
В: Где хранить тест-кейсы?
О: В репозитории рядом с фичей (markdown/BDD-файлы), линк в RTM/тикет. Так проще поддерживать актуальность.
Шпаргалка (распечатайте)
- Для каждого параметра: 2–3 валидных + 2–4 невалидных класса.
- Для чисел/дат/строк — тестируйте мин-1, мин, мин+1, макс-1, макс, макс+1.
- Всегда добавляйте негативы: auth, RLS, идемпотентность, таймауты, rate-limit.
- Оракулы: ответ, БД, событие, логи/аудит, метрики.
- Привязывайте всё к RTM и AC; фиксируйте, какие SLO наблюдаете.
- Минимум PII в тест-данных; стабильные идентификаторы и «сидовые» наборы данных.



