Всім привіт, мене звати Ігор, я PHP-розробник у компанії Binariks. У цій статті я розповім вам про можливості тестування, які надає фреймворк Laravel в поєднанні з PHPUnit, тому запарюйте чайок та готуйтесь до лонгріду 🙂
Наявність валідних тестів з хорошим покриттям — одне з правил якісного коду. З їх допомогою можна швидко виявити проблеми в функціоналі, відповідно й прискорити вихід функціонала в прод. Вони спрощують життя QA-команді, зменшуючи кількість однотипного мануального тестування, тим самим зменшуючи вплив людського фактора.
В залежності від ступеня ізоляції тести розділяють на наступні типи:
Перед тим, як почати розповідь про можливості фреймворку, думаю, варто нагадати правила хороших тестів:
Хороший тест має три стадії:
Підтримка тестування за допомогою PHPUnit включена «з коробки», а файл phpunit.xml
уже налаштовано для вашої програми. Також у фреймворк додано багато допоміжних методів, які є зручними й спрощують тестування.
За замовчуванням каталог tests
в Laravel містить дві папки Feature і Unit.
Юніт-тести призначені для тестування маленької ізольованої частини вашого коду. Окремого методу класу чи функції.
Тести в директорії Unit не ініціюють ваш Laravel-додаток, тому з юніт-тестами ви не зможете отримати доступ до сервісів Laravel чи бази даних.
Наведу приклад написання юніт-тестів з практичним застосуванням вищеперелічених правил.
Завдання: імплементувати метод getSubscription
в класі SubscriptionService
, який буде пропонувати користувачеві підписки в залежності від типу його акаунту:
Відповідно до методології TDD почнемо з написання тестів і описуємо очікувану поведінку метода:
Створюємо сам функціонал:
Викликаємо тести:
Юніт-тести корисні для перевірки роботи окремих важливих частин коду. Вони швидкі і пишуться відносно просто, дають високу стабільність коду, що покритий тестами.
Проте їх ізольованість має і недоліки, а саме — вони не можуть гарантувати коректну взаємодію всіх окремо протестованих частин коду, при реальних сценаріях, коли код не є ізольованим.
На відміну від директорії Unit, тести в каталозі Feature призначені для тестування взаємодії різних компонентів програми. Вони ініціюють ваш Laravel-додаток.
Відповідно, з допомогою цих тестів можна перевіряти більшу частину вашого коду, починаючи від окремих методів, які працюють в інфраструктурі Laravel, те, як кілька об’єктів взаємодіють один з одним або навіть повний запит HTTP включно з відповіддю з сервера.
Згідно з рекомендаціями розробників фреймворку Laravel, більшість ваших тестів мають бути Feature-тестами. Тому що ці типи тестів забезпечують більшу впевненість у тому, що ваша система функціонує належним чином.
Написання Feature-тестів має кілька особливостей:
PHPunit.xml
дозволить задати чи перезаписати всі .env
-змінні (в тому числі і конфігурації, які відповідають). Крім того, ви можете створити файл .env.testing
— в такому випадку він буде використовуватися замість .env
-файлу при тестуванні.Illuminate\ Foundation\ Testing\ RefreshDatabase
, який здійснює всі міграції та ініціює транзакцію, яка поверне вашу базу даних до вихідного стану після завершення тесту.Тестування компонента. Приклад: в модель User
потрібно додати scope
, який буде фільтрувати записи в базі даних за полем is_admin
:
scope
буде передаватися true
— повертати всі записи, в яких поле is_admin true
;scope
буде передаватися false
— повертати всі записи, в яких поле is_admin false
.Напишемо тест:
Додамо відповідний scope
в модель User:
Переконаємося, що тест працює коректно:
Додамо тест для другого випадку:
Перевіримо обидва випадки:
Тестування АРІ. Приклад:
user_id
(id залогованого користувача), title
, text
);users.is_admin === true
);{ “user_id”: (id користувача), “id”: (article id), “title”: (поле title) } <?php namespace Tests\Feature; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithFaker; use Tests\TestCase; class CreateArticleTest extends TestCase { use RefreshDatabase; use WithFaker; public function setUp(): void { parent::setUp(); // виключаємо з тесту всі мідлвер, що не відносяться до тесту $this->withoutMiddleware(); } public function testCreateArticleSuccess() { // Створюємо користувача-адміна $user = User::factory()->create([ 'is_admin' => true, ]); // З допомогою фейкера генеруємо дані запиту $request = [ 'title' => $this->faker()->text(6), 'text' => $this->faker()->text(50), ]; // З допомогою методу actingAs() імітуємо поведінку запиту для адміна $response = $this->actingAs($user) // виконуємо сам запит ->post( // перший параметр - роут згенерований з допомогою Laravel функціоналу Named routes route('articles.create'), // Другий параметр - request body $request, ); // Перевірка результату: // Перевіряємо, чи відповідь з сервера 200 $response->assertStatus(200) // Перевірка структури відповіді ->assertJsonStructure([ 'id', 'user_id', 'title', ]) // перевірка достовірності отриманих даних ->assertJson([ 'user_id' => $user->id, 'title' => $request['title'], ]) // Конвертуємо відповідь в array, щоб отримати id статті ->json(); // Перевіряємо, чи був створений запис в базі даних $this->assertDatabaseHas('articles', [ 'id' => $response['id'], 'user_id' => $response['user_id'], 'title' => $response['title'], 'text' => $request['text'], ]); } }
Laravel «з коробки» має багато методів, що будуть корисні при тестуванні. Коротко пройдуся по них і додам корисні посилання (на документацію 🙂 ):
1) HTTP-тести. Назва каже сама за себе — їх мета провести тестування конкретних ендпойнтів сервера. Часто при тестуванні доводиться стикатися з наступними методами фреймворку:
withoutMiddleware()
— допомагає відключити всі чи задані Middleware
;withHeaders()
, withSession()
, withCookies()
— імітують наявність в запиті певних хедерів, сесій, кукі;actingAs()
— імітує поведінку сервера при певному авторизованому користувачеві;get()
, post()
, put()
, patch()
, delete()
, json()
— імітує відповідні методи виклику севера;assertStatus()
, assertJson()
, assertJsonStructure()
— надають можливість перевірки статус відповіді з сервера та його структури;view()
, blade()
— можливість перевірки згенерованих фреймворком сторінок.2) Методи для імітації роботи функціонала:
4) Методи для тестування консолі:
В цій статті я намагався коротко описати загальні хороші практики, які варто використовувати при написанні тестів, та показати приклади різних типів тестів, та їх імплементації в середовищі Laravel, зробити короткий огляд можливостей тестування в середовищі фреймворка.
Сподіваюся, ця стаття буде вам корисною. Всім успіхів!
Читайте також: Як написати односторінковий додаток на Laravel та Vue.js за 45 хвилин
Дуже хочеться робити якісь десктопні апки. Сумую за часами коли всі програми були offline-first, і…
Надсилаючи криптовалюту, багато новачків ставлять запитання: як працюють комісії та чому вони відрізняються в різних…
Нова афера набирає обертів — ось детальний розбір того, як фальшиві потенційні роботодавці намагаються вкрасти…
Соцмережа з можливістю вбудовувати повноцінні додатки прямо в пости — звучить як фантастика, але Farcaster…
Я ніколи в житті не був на співбесіді «по ту сторону». Мене ніхто не запрошував…
Я багато писав про fly.io — тоді ще новачка на ринку IaaS/PaaS хостингу. Я й…