Скільки справжнього коду в тесті
Вид тесту визначає межа: що тест запускає по-справжньому, а що лишає за дужками. бере один клас або невелику групу класів без бази, мережі й фреймворку. додає одну справжню залежність: базу, кеш, файлову систему чи HTTP-клієнт. у Laravel надсилає запит крізь увесь застосунок, від маршруту до відповіді. [[E2E-тест|e2e]] відкриває справжній браузер і клікає по сторінці.
Чим більше справжнього коду, тим більше тест ловить і тим дорожче коштує. Unit-тест іде мілісекунди й падає лише від зміни логіки в своєму класі, зате не помітить зламаної міграції. Feature-тест побачить і міграцію, і валідацію, і права доступу, але йде в десятки разів довше, а коли падає, причину доводиться шукати по всьому стеку. E2E ловить ще й зламаний JavaScript, проте найповільніший і найчастіше стає .
У Laravel тести лежать у двох папках, і в проєкті з Pest різницю між ними задає tests/Pest.php. Його типовий вміст розширює класом Tests\TestCase лише tests/Feature: там є контейнер, фасади, конфіг, база й методи на кшталт $this->get(). Тести з tests/Unit цього класу не отримують і працюють як чистий PHPUnit, тож виклик фасаду там упаде з «A facade root has not been set». Integration-тести в Laravel зазвичай живуть у tests/Feature, бо їм потрібні контейнер і база.
Скільки тестів якого виду писати, сперечаються давно. радить багато unit-тестів і мало повільних, ставить у центр інтеграційні, бо вони ближчі до того, що бачить користувач. Обидві форми є на хабі розділу. На практиці відповідь залежить від того, де живе логіка: багатий домен добре покривається unit-тестами, а CRUD-контролер чесніше перевірити одним feature-тестом.
Одне правило на трьох рівнях
Правило: вакансія зникає з каталогу через 60 днів після публікації. Його можна перевірити в доменному обʼєкті, у запиті до бази й на сторінці /jobs. Перемикайте вкладки й дивіться, скільки коду бере участь у кожному тесті і що саме може зламатися.
Feature-тест сторінки вакансії
Вилку зарплати в PHP Ukraine перевіряють на двох рівнях. Unit-тест tests/Unit/Hiring/SalaryTest.php дивиться на сам value object: Salary::range(7000, 5000) міняє межі місцями й рахує середину. Feature-тест нижче проходить увесь шлях: вакансія приходить через справжній сценарій синхронізації в базу, сторінка віддається по HTTP, а вилка мусить опинитися в розмітці JobPosting для Google. Базу для кожного такого тесту готує tests/Pest.php: він підключає TestCase і RefreshDatabase лише для tests/Feature.
Де ламається баланс
Усе через HTTP
Кожне правило перевіряють feature-тестом: двадцять запитів до /checkout з різними сумами, щоб покрити тарифи доставки. Набір іде хвилинами, а коли тест падає, незрозуміло, зламався тариф, валідація чи шаблон.
Натомість: варіації правила перевіряйте unit-тестами на доменному класі, а на рівні HTTP лишіть один-два сценарії, які доводять, що все зʼєднано правильно.
Unit-тести, що нічого не ловлять
Контролер, який лише передає запит у сервіс, обкладений моками з усіх боків. Тест повторює реалізацію, проходить завжди й падає лише від рефакторингу. Зламаний маршрут чи міграцію він не помітить.
Натомість: тонкий код, що склеює фреймворк з доменом, перевіряйте feature-тестом. Unit-тести пишіть там, де є логіка.
Unit-тест, що потай ходить у базу
Тест лежить у tests/Unit, але через модель Eloquent чи хелпер now() тягне за собою Laravel і базу. Він повільний, залежить від порядку запуску й ламається від чужих змін.
Натомість: у tests/Unit тримайте лише код без фреймворку. Щойно тесту потрібні контейнер чи база, переносьте його в tests/Feature, а залежність від часу передавайте параметром або через Clock.
E2E на кожну кнопку
Браузерні тести перевіряють валідацію кожного поля форми. Вони йдуть хвилинами, падають від повільної анімації, і команда звикає перезапускати червоний CI.
Натомість: у браузері перевіряйте кілька критичних шляхів, як реєстрація чи оплата, і те, що без JavaScript не перевірити. Валідацію лишіть feature-тестам.
Який тест писати
- Unit-тест
- Тест одного класу або невеликої групи класів у памʼяті, без бази, мережі, файлів і фреймворку. Залежності, що виходять назовні, підміняють дублерами.
- Integration-тест
- Тест, який перевіряє ваш код разом з однією справжньою зовнішньою залежністю: базою, кешем, чергою, файлами чи чужим API.
- Feature-тест
- У Laravel: тест, що надсилає HTTP-запит або запускає команду крізь увесь застосунок, з маршрутизацією, middleware, валідацією й базою. Деінде його звуть функціональним або end-to-end на рівні HTTP.
- E2E-тест
- End-to-end: справжній браузер відкриває сторінку, клікає й заповнює форми, як користувач. У PHP це Laravel Dusk, браузерні тести Pest або Playwright окремо від PHP.
- Flaky-тест
- Тест, який то проходить, то падає на тому самому коді. Причини: час, випадкові дані, порядок тестів, мережа, гонки в браузері.
- Піраміда тестів
- Модель Майка Кона (Mike Cohn): широка основа з unit-тестів, менше інтеграційних, зовсім мало e2e.
- Трофей тестів
- Модель Кента Доддса (Kent C. Dodds): статичний аналіз в основі, найбільше інтеграційних тестів, менше unit і e2e.
Питання зі співбесід
Unit-тест перевіряє клас у памʼяті без фреймворку й бази та йде мілісекунди. Feature-тест розширює Tests\TestCase, піднімає застосунок і надсилає запит крізь маршрути, middleware, валідацію й базу. Перший точно показує, яке правило зламалось, другий доводить, що все зʼєднано правильно.
Бо tests/Pest.php розширює класом Tests\TestCase лише tests/Feature. Без нього ніхто не створює застосунок і не реєструє його в фасадах, тож Cache::get() падає з «A facade root has not been set». Тест, якому потрібен Laravel, належить до tests/Feature.
Integration-тест перевіряє ваш код разом з однією справжньою залежністю, наприклад репозиторій на справжній базі. Unit-тест з фейком не побачить помилки в SQL, а feature-тест побачить, але через десяток шарів, і ви довго шукатимете причину. Integration-тест ловить її точно й швидше.
Залежить від того, де живе ризик. Якщо в домені багато правил, основу дають unit-тести, бо кожна варіація коштує мілісекунди. Якщо застосунок переважно CRUD і логіка в запитах і валідації, вигідніші feature-тести, як радить трофей. В обох випадках e2e лишають для кількох критичних шляхів, а статичний аналіз ловить цілий клас помилок без жодного тесту.