Начните тестировать 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.
До следующей встречи!