Всем привет, меня зовут Игорь, я 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:
Убедимся, что тест работает корректно:
Добавим тест для второго случая:
Проверим оба случая:
Тестирование АРI. Пример:
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 минут
В благословенные офисные времена, когда не было большой войны и коронавируса, люди гораздо больше общались…
Вот две истории из собственного опыта, с тех пор, когда только начинал делать свою карьеру…
«Ты же программист». За свою жизнь я много раз слышал эту фразу. От всех. Кто…
Отличные новости! Если вы пропустили, GitHub Copilot — это уже не отдельный продукт, а набор…
Несколько месяцев назад мы с командой Promodo (агентство инвестировало в продукт более $100 000) запустили…
Пару дней назад прочитал сообщение о том, что хорошие курсы могут стать альтернативой классическому образованию.…