Начните тестировать Laravel код меньше чем за 5 минут
Зачем писать тесты
Написание тестов для своих проектов необходимо по ряду причин, в том числе:
- Исправление багов: Вы можете выявить и исправить основные ошибки на этапе разработки, прежде чем они испортят впечатление пользователей.
- Уверенное развёртывание: Хорошо протестированное приложение можно развернуть, зная, что его код надёжен.
- Избавление от тестирования в браузере: Выполнение одних и тех же действий вручную может отнимать много времени. Автоматизированное тестирование позволяет сэкономить много времени и сил.
- Довольные заказчики: Хорошо протестированное приложение обеспечивает более плавное взаимодействие с пользователем, что приводит к увеличению числа довольных заказчиков и снижению оттока.
- Довольные клиенты: Клиенты ценят приложение, в котором нет очевидных багов!
- Довольный работодатель: Хорошо протестированное и отлаженное приложение порадует и вашего работодателя. Оно демонстрирует профессионализм и внимание к деталям. Это только положительно сказывается на Вашей карьере.
Создание нового проекта
Создать новый проект в 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
мы можем написать необходимую логику для отправки электронной почты. В рамках этого контроллера мы должны выполнить три действия:
- Проверить правильность вводимых пользователем данных, поскольку доверять им не следует.
- Отправить по электронной почте письмо, используя фасад
Mail
иContactMail
. - Перенаправить пользователя на предыдущую страницу с сообщением о статусе.
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);
});
- Сначала мы подделываем фасад
Mail
, чтобы предотвратить отправку реальных писем, и утверждаем, что письмо было отправлено. - Затем выполняем POST-запрос к маршруту
/contact
со случайно сгенерированным фальшивым именем, электронной почтой и сообщением. - Мы утверждаем, что запрос перенаправлен обратно.
- Наконец, мы утверждаем, что рассылка
ContactMail
была отправлена.
Мы на зелёном! Продолжаем.
Контактное сообщение электронной почты требует указания имени
Теперь мы должны проверить, что для сообщения электронной почты требуется имя, валидный адрес электронной почты и сообщение.
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...
});
- Мы выполняем POST-запрос к маршруту
/contact
с указанием только фальшивого адреса электронной почты и сообщения (без имени). - Затем мы утверждаем, что ответ недействителен и что поле
name
является обязательным.
Будьте прагматичны при написании тестов
Моя стратегия написания тестов остаётся неизменной на протяжении многих лет:
- Пишите для всех "счастливых путей". "Счастливый путь" представляет собой наилучший, ожидаемый путь через ваш код.
- Пишите для очевидных "несчастливых путей". Ожидайте неожиданного. Именно поэтому мы готовим тесты на отсутствие входных данных.
- Напишите тесты для багов, с которыми сталкиваются ваши пользователи. Они называются регрессионными тестами. Запишите баг как тест, исправьте его, вернитесь к зелёному цвету и убедитесь, что он больше никогда не повторится.
Мы поможем вам
Если вы хотите углубиться, мы, конечно же, поможем вам в Laracasts. Вы можете начать с начального курса по Pest, например, Pest From Scratch. Когда вы почувствуете себя комфортно и будете готовы работать над реальными приложениями, переходите к изучению Pest-Driven Laravel.
До следующей встречи!