Начните тестировать Laravel код меньше чем за 5 минут

Источник: «Start Testing Your Laravel Code in Less Than 5 Minutes»
Кодирование — это весело. А вот отладка? …Не очень. Именно поэтому тестирование имеет решающее значение для успеха любого нетривиального проекта. В этой статье я покажу как легко начать тестировать свои Laravel приложения. Давайте растопим лёд раз и навсегда!

Зачем писать тесты

Написание тестов для своих проектов необходимо по ряду причин, в том числе:

Создание нового проекта

Создать новый проект в Laravel с помощью Pest очень просто — достаточно выполнить одну команду. Откройте терминал и запустите команду:

laravel new hello-world --pest

На момент написания этой статьи Laravel поставляется с PHPUnit по умолчанию. Однако в последние несколько лет все большую популярность приобретает Pest, основанный на PHPUnit. Давайте воспользуемся флагом --pest для генерации тестов, специфичных для Pest.

Pest — фреймворк для тестирования, ориентированный на простоту и тщательно разработанный для того, чтобы вернуть радость тестирования на PHP.

Перейдите в новый проект hello-world, убедитесь, что в браузере успешно отображается заставка приветствия Laravel, и переходите к следующему разделу!

Убедитесь, что Pest запускается

Теперь можно выполнить пробные тесты проекта, чтобы убедиться, что все работает так, как ожидалось.

Просмотрите файл tests/Feature/ExampleTest.php.

<?php

it('returns a successful response', function () {
$response = $this->get('/');

$response->assertStatus(200);
});

Обратите внимание на то, что название метода прекрасно описывает смысл теста. Постарайтесь привить себе привычку делать то же самое для своих проектов.

Запустите набор тестов с помощью следующей команды:

php artisan test

Все должно быть зелёного цвета. Предполагая, что среда настроена правильно, мы смогли установить свежее приложение Laravel и запустить начальный набор тестов всего за пару минут. Неплохо!

Такой базовый тест гарантирует, что если что-то сломается во время рендеринга домашней страницы, то набор будет корректно завершён. Давайте попробуем это сделать. Настроим ExampleTest.php так, чтобы он ожидал ответа с другим кодом состояния. Как насчёт 302?

 <?php

it('returns a successful response', function () {
$response = $this->get('/');

- $response->assertStatus(200);
+ $response->assertStatus(302);
});

Запустите php artisan test ещё раз, и, как и ожидалось, он не работает:

php artisan test

FAILED Tests\Feature\ExampleTest > it returns a successful response
Expected response status code [302] but received 200.
Failed asserting that 302 is identical to 200.

at tests/Feature/ExampleTest.php:6
2▕
3▕ it('returns a successful response', function () {
4▕ $response = $this->get('/');
5▕
➜ 6▕ $response->assertStatus(302);
7▕ });
8▕


Tests: 1 failed, 1 passed (2 assertions)
Duration: 0.18s

Реальный пример

Разумеется, практика помогает совершенствоваться. В следующем разделе мы рассмотрим, как можно написать тесты, которые в большей степени соответствуют реальному приложению. Мы создадим тестируемую форму обратной связи, доставляющую письмо получателю.

Контроллер

Для начала работы с формой обратной связи создадим контроллер SendContactEmailController.

php artisan make:controller SendContactEmailController --invokable

Обратите внимание, что мы используем флаг --invokable для создания контроллера с одиночным действием, или контроллера, который может иметь только одно действие.

Маршруты

Далее следует объявить два RESTful-маршрута для работы с формой обратной связи: один отвечает на GET-запросы по пути /contact. Именно здесь мы будем отображать форму. Далее мы прослушиваем POST-запросы по пути /contacts. Здесь мы можем получить данные от пользователя и передать сообщение.

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\SendContactEmailController;

Route::view('/contact', 'contact');
Route::post('/contact', SendContactEmailController::class);

Создание почтовой рассылки

Создание рассылки в Laravel так же просто, как и создание любого другого контроллера или модели. Мы можем использовать команду make:mail.

php artisan make:mail ContactMail

Немного модифицируем его с помощью трёх свойств: $from, $name и $message. В приведённом далее примере мы используем определение свойств в конструкторе, что позволяет немного сократить объем кода.

namespace App\Mail;

use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;

class ContactMail extends Mailable
{
use Queueable, SerializesModels;

public function __construct(
protected string $from,
protected string $name,
protected string $message
) {
}

public function envelope(): Envelope
{
return new Envelope(
from: new Address($this->from, $this->name),
subject: 'Contact Mail',
);
}
}

Метод с красивым названием envelope(), как вы уже догадались, позволяет нам настраивать детали электронного письма, такие как тема и адрес отправителя.

Создание формы

Мы почти у цели. Теперь нужно построить саму форму обратной связи. Не волнуйтесь, здесь нет ничего уникального или фантастического. Мы добавим поля для имени, электронной почты и сообщения.

php artisan make:view contact

В результате выполнения этой команды должен появиться файл resources/views/contact.blade.php. В него вставьте следующий HTML-код:

@if (session('status'))
<p>{{ session('status') }}</p>
@endif

<form method="POST" action="/contact">
@csrf

<div>
<label for="name">Name</label>
<input type="name" id="name" name="name" value="{{ old('name') }}" />
@error('name') <p>{{ $message }}</p> @enderror
</div>

<div>
<label for="email">Email</label>
<input type="email" id="email" name="email" value="{{ old('name') }}" />
@error('email') <p>{{ $message }}</p> @enderror
</div>

<div>
<label for="message">Message</label>
<textarea id="message" name="message">{{ old('message') }}</textarea>
@error('message') <p>{{ $message }}</p> @enderror
</div>

<button>Send</button>
</form>

Скорее всего, вы писали подобный HTML код бесчисленное количество раз. Однако обратите внимание, что мы используем директиву Laravel @error() для отображения сообщений об ошибках валидации под каждым полем ввода. Также мы включили директиву @csrf для защиты от подделки межсайтовых запросов.

Подделка межсайтовых запросов (CSRF) — атака, заставляющая конечного пользователя выполнить нежелательные действия в веб-приложении, в котором он в данный момент аутентифицирован.

Отправка электронной почты

В контроллере SendContactEmailController мы можем написать необходимую логику для отправки электронной почты. В рамках этого контроллера мы должны выполнить три действия:

namespace App\Http\Controllers;

use App\Mail\ContactMail;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;

class SendContactEmailController extends Controller
{
public function __invoke(Request $request)
{
$validated = $request->validate([
'email' => 'required|email',
'name' => 'required|string',
'message' => 'required|string',
]);

Mail::to($validated['email'])
->send(new ContactMail(
$validated['name'],
$validated['message'],
));

return back()->with('status', 'Your email has been sent!');
}
}

Напишем тесты

Создать новые тесты в Laravel очень просто с использованием следующей команды. Не забудьте про опцию --pest.

php artisan make:test SendContactEmailTest --pest

Теперь мы можем окончательно подготовить тесты для нашей формы обратной связи.

Страница обратной связи работает

Начнём с простого теста, который проверяет, возвращается ли код состояния 200 при посещении страницы /contact.

use App\Mail\ContactMail;
use Illuminate\Support\Facades\Mail;
use function Pest\Laravel\{get,post};

test('the contact page works', function () {
get('/contact')->assertOk();
});

Легко! Мы написали первый тест, проверяющий доступность страницы обратной связи. Этот тест выполняет GET-запрос к маршруту /contact и проверяет, что статус ответа — OK (200).

Контактное сообщение электронной почты может быть отправлено

Далее мы подтвердим, что контактное сообщение электронной почты может быть отправлено.

test('the contact email can be sent', function () {
Mail::fake();

post('/contact', [
'name' => fake()->name(),
'email' => fake()->safeEmail(),
'message' => fake()->paragraph(),
])
->assertRedirect('/contact');

Mail::assertSent(ContactMail::class);
});

Мы на зелёном! Продолжаем.

Контактное сообщение электронной почты требует указания имени

Теперь мы должны проверить, что для сообщения электронной почты требуется имя, валидный адрес электронной почты и сообщение.

test('the contact email requires a name', function () {
post('/contact', [
'email' => fake()->safeEmail(),
'message' => fake()->paragraph(),
])
->assertInvalid(['name' => 'required']);
});

test('the contact email requires a valid name', function () {
// rinse and repeat...
});

Будьте прагматичны при написании тестов

Моя стратегия написания тестов остаётся неизменной на протяжении многих лет:

Мы поможем вам

Если вы хотите углубиться, мы, конечно же, поможем вам в Laracasts. Вы можете начать с начального курса по Pest, например, Pest From Scratch. Когда вы почувствуете себя комфортно и будете готовы работать над реальными приложениями, переходите к изучению Pest-Driven Laravel.

До следующей встречи!

Дополнительные материалы

Предыдущая Статья

Новое в Symfony 6.4: Улучшения каталога сборки

Следующая Статья

Восемь способов писать чистый и лаконичный код в Laravel