Flaky-тест то проходить, то падає на тому самому коді. Найчастіші причини: прихований час (`now()`, `new DateTimeImmutable()`), випадкові дані, залежність від порядку тестів і спільного стану, мережа й паралельність. Час роблять явною залежністю (порт Clock, PSR-20, `FrozenClock`) або зупиняють через `travelTo()`/`freezeTime()`, випадковість передають як `Random\Randomizer` з seed, а порядок перевіряють запуском з `--order-by=random`.
Тестування для Middle: питання на співбесіду
6 питань рівня Middle з теми Тестування з розгорнутими відповідями, порадами та перевіркою.
Підміняють те, що повільне, недетерміноване або має побічні ефекти: базу, мережу, пошту, час, випадковість. Value objects і доменні класи беруть справжні. Чужі бібліотеки не мокають напряму: їх загортають у власний вузький порт, домен тестують з фейком цього порту, а сам адаптер integration-тестом проти справжньої бібліотеки чи записаних відповідей.
TDD це цикл з трьох кроків: написати маленький тест, що падає з очікуваної причини (red), найпростішим кодом зробити його зеленим (green), прибрати дублювання й покращити дизайн під захистом тестів (refactor). Головна користь у фідбеку на дизайн, покриття тут лише побічний ефект: код, який важко протестувати першим, зазвичай має забагато залежностей. Найкраще працює для доменної логіки з чіткими правилами, гірше для дослідницького коду й UI.
Покриття рахує, які рядки виконались під час тестів, але не чи помітив би тест їхню зміну: тест без перевірок дає 100%. Мутаційне тестування вносить у код дрібні зміни (мутанти: `>=` на `>`, `+` на `-`, `true` на `false`) і проганяє тести; мутант, на якому все зелене, вижив і показує неперевірене місце. У PHP це Infection і вбудований у Pest `--mutate`; частку вбитих мутантів показує MSI.
Архітектурний тест перевіряє будову коду, а не поведінку: хто кого імпортує, чи класи final, чи є strict_types, чи не лишився `dd()`. У Pest це `arch()->expect('App\Domain')->not->toUse('Illuminate')`: файли читаються парсером, код не виконується. Ними охороняють межі шарів і модулів, які інакше тихо зникають, а наявні порушення вносять у `ignoring()` явним списком.
Кожен тест має сам створювати рівно ті дані, від яких залежить його перевірка, і показувати їх у тексті тесту. Для моделей це фабрики зі станами (`Vacancy::factory()->published()`), для доменних обʼєктів у unit-тестах builder або Object Mother з розумними значеннями за замовчуванням. Спільні фікстури на весь набір зручні лише для довідників, бо інакше тести неявно залежать від чужих даних і ламаються від кожної зміни.