Стратегія тестування ядра
1) Принципи
Пірамідально-трофейний баланс. База - швидкі модульні та контрактні тести; вище - компонентні та інтеграційні; на вершині - мінімальний, але цінний шар e2e.
Shift-left. Чим раніше ловимо дефект (лінтер, статичний аналіз, property-based), тим дешевше.
Deterministic by design. Керуємо часом, мережею, рандомом і зовнішніми залежностями.
Економіка якості. Будь-який тест - це «страховка»: мета - мінімізувати сумарні витрати (дефекти + супровід тестів).
Ризик-орієнтованість. Покриття концентрується на бізнес-інваріантах і протоколах (контракти, ідемпотентність, консистентність).
2) Рівні тестування та зони відповідальності
2. 1 Unit (модульні)
Перевіряють чисту логіку без I/O.
Миємо тільки межі (порт/адаптер), використовуємо фабрики для даних.
Швидкі (≤50 -100 мс/тест), паралеляться.
2. 2 Contract (постачальник ↔ споживач)
Фіксують API-контракти (HTTP/gRPC/event) між сервісами.
Використовуємо consumer-driven підхід: контракти зберігаються в VCS, перевіряються в CI постачальника.
Зменшують крихкість інтеграційних e2e.
2. 3 Component (над модулем, з реальним сховищем)
Запускаємо частину сервісу з реальною БД/кешем в контейнері (Testcontainers).
Валідуємо міграції схем, індекси, транзакції, блокування.
2. 4 Integration/System (наскрізні шляхи між сервісами)
Піднімаємо набір сервісів в ізольованому середовищі.
Перевіряємо наскрізні інваріанти: транзакційність, ретраї, ідемпотентність, обробку помилок.
2. 5 E2E (мінімальний «цінний» шар)
Реальні протоколи і оточення «як у проді», але обмежений сценарний набір: оплата → підтвердження → проводка; реєстрація → верифікація → вхід.
Використовуємо для випуску релізів і регресу високоризикових фіч.
3) Тестопридатна архітектура
Порти/адаптери (Hexagonal). Бізнес-ядро не знає про HTTP/SQL; залежності впроваджуються через інтерфейси.
Ін'єкція часу/рандома.'Clock','Random'- залежності; в тестах фіксуємо.
Конфігурована I/O абстракція. Черги, БД, KMS - через інтерфейси з тестовими реалізаціями.
Функціональні інваріанти. Явно формулюємо постумови і предикати - їх простіше тестувати і моніторити.
4) Дані для тестів
Фабрики/білдери замість статичних JSON-фікстур: менше крихкості.
Ідемпотентні сиди і reset-хук БД перед тестом (migrations → truncate → seed).
Каталоги кейсів: «норми», «краї», «помилки», «хаос».
Синтетика замість реальних ПД: генератори, маскування, профілі приватності.
5) Конкуренція та ідемпотентність
Тести на гонки (race): конкурентні записи/резерви/блокування.
Перевірка ідемпотентних ключів (наприклад,'( operation, external_id)'): повторні виклики не змінюють стан.
Ретраї і таймаути: гарантуємо коректність при тимчасових помилках.
dedupe_key = hash(op + external_id)
if exists(executions, dedupe_key): return previous_result else:
reserve(dedupe_key)
result = do_operation()
store(executions, dedupe_key, result)
return result
6) Час, таймаути, часові пояси
Всі збережені часи - UTC; в тестах використовуємо'FixedClock'.
Тестуємо DST-кейси (дублі/пропуски годинників), вікна «локального дня».
Таймаути перевіряємо монотонічними годинниками; симулюємо NTP-тремтіння.
7) Стійкість і хаос
Fault-injection: мережеві помилки, 5xx, затримки, часткова деградація (кеш недоступний).
Chaos-тести в середовищі pre-prod: відключення вузлів, перевантаження черг, розриви BGP/Anycast (емуляція).
Fallback-політики і деградація UX: тести повинні підтверджувати коректний «graceful degradation».
8) Продуктивність
Мікро-бенчмарки для критичних алгоритмів (з фіксацією CPU/alloc).
Навантажувальні профілі: baseline (p50/p95), стрес (пік), продовжені (soak) для витоків пам'яті.
Регрес-гейти: build провалюється, якщо p95 латентності гірше baseline> X%.
9) Безпека та відповідність
SAST/Lint: пошук вразливостей/антипатернів.
DAST/IAST: базові сценарії на стенді (XSS/SQLi/SSRF-проби).
Secrets-scan: відсутність ключів/паролів в коді та артефактах.
Privacy-тести: відсутність ПД в логах/трасуваннях, дотримання «управління згодою», профілі анонімізації для вивантажень.
10) Метрики якості та SLO
Test pass rate і flaky index (кількість нестабільних тестів/тиждень).
Coverage-цільове:- 90-100% для критичних модулів ядра,
- 70-80% для периферії (з фокусом на інваріанти).
- Release risk score: сукупність: зміни в критичних файлах × падіння бенчмарків × нові flaky.
- Помилковий бюджет: зв'язка прод-SLO (аптайм/помилки) з експериментами і частотою релізів.
11) CI/CD і гейти
Матриця стадій:1. Lint/Format/TypeCheck
2. Unit + Property-based
3. Contract provider/consumer
4. Component (Testcontainers)
5. Integration + Perf smoke
6. Security (SAST/Secrets)
7. Build/Package + SBOM
8. Deploy to pre-prod + e2e + chaos smoke
Гейти: стоп по падінню контрактів, зростанню латентності, новим критичним вразливостям.
Кеш і шардинг: прискорюємо pipeline за рахунок паралелізму та інкрементальних прогонів (за зміненими модулями).
12) Flaky-тести: виявлення та лікування
Автоповтор + кворум (2/3 прогонів).
Детектор флаки-патернів: залежність від часу/рандома/неявних очікувань.
Карантин з SLA: тест не блокує релізи, але зобов'язаний бути виправлений/переписаний в N днів.
Нульова толерантність до флаки в «ядрі» критичного шляху.
13) Property-based, мутаційне та фазз-тестування
Property-based: формулюємо властивості (комутативність, ідемпотентність, монотонність), генератори граничних даних.
Mutation testing: вимірюємо «силу» тестів (чи вбивають вони внесені мутації).
Fuzzing: протоколи/парсери/формати (JSON, Protobuf, CSV), особливо на кордонах безпеки.
prop "serialize/deserialize roundtrip":
forAll(randomModel()):
decode(encode(model)) == model
14) Спостережуваність і зв'язок з тестами
Трасування тестів (trace-id в логах): зручно реплеїти в pre-prod.
Снапшоти метрик при перформанс-прогоні - зберігаємо як артефакт.
Контроль логів: відсутність чутливих полів, розмір логів в межах SLO.
15) Документація та процедури
Test Handbook: де які тести запускати, як писати фабрики, як оновлювати контракти.
Runbooks: реплей інциденту, швидка діагностика, відкат релізу.
Каталог інваріантів: список системних гарантій і посилань на відповідні тести/алерти.
16) Чек-лист архітектора
1. Описані інваріанти ядра і критичні шляхи?
2. Є матриця рівнів тестів і їх SLO (час, стабільність)?
3. Контракти версіонуються і валідуються в CI у постачальника і споживача?
4. Час/рандом/мережа контрольовані в тестах (FixedClock, Fault-injector)?
5. Налаштовані Testcontainers/ізольована БД, міграції перевіряються?
6. Є перформанс-базлайни і гейт на регресію?
7. Включені SAST/Secrets-scan і privacy-перевірки логів?
8. Ведеться облік flaky і є SLA на виправлення?
9. Зв'язок тестів з прод-SLO і помилковим бюджетом прозоро оформлена?
10. Документовані runbook'і і каталог інваріантів?
Висновок
Стратегія тестування ядра - це не список інструментів, а архітектурна здатність: тестопридатний дизайн, сувора ієрархія рівнів, керовані дані, відмовостійкість і метрики, вбудовані в CI/CD. Слідуючи описаним практикам, команда отримує швидкий і надійний зворотний зв'язок, а релізи стають передбачуваними і безпечними.