Привет, Хабр!
Laravel — это целая экосистема, которая позволяет разрабатывать качественные веб-приложения. Но вот вопрос: как быть уверенным, что все эти красивые строки кода не развалятся в самый неподходящий момент? Ответ очевиден — тесты!
Какие тесты бывают в Laravel
Laravel поддерживает три главных типа тестов: юнит-тесты, интеграционные тесты и функциональные тесты. Каждому своё, каждый важен.
Юнит-тесты
Юнит-тесты — маленькие тесты для маленьких частей кода: методов, функций, классов. Они изолированы от всего остального — ни базы данных, ни API не трогаем, только чистая логика.
Пример? Пожалуйста:
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Services\DiscountService;
class DiscountServiceTest extends TestCase
{
public function test_calculate_discount()
{
$price = 100;
$expectedDiscount = 90; // 10% скидки
$service = new DiscountService();
$actualDiscount = $service->calculateDiscount($price);
$this->asnamespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Services\DiscountService;
class DiscountServiceTest extends TestCase
{
public function test_calculate_discount()
{
$price = 100;
$expectedDiscount = 90; // 10% скидки
$service = new DiscountService();
$actualDiscount = $service->calculateDiscount($price);
$this->assertEquals($expectedDiscount, $actualDiscount);
}
}
sertEquals($expectedDiscount, $actualDiscount);
}
}
Здесь тестируем метод calculateDiscount
. 100 рублей на входе — 90 на выходе. Всё по плану.
Интеграционные тесты
Интеграционные тесты — это когда ты смотришь, как всё взаимодействует в связке. Вот у тебя есть сервис, который лезет в БД, обращается к внешнему API и возвращает что-то вкусное. Тут юнит-тесты уже не помогут — нужно посмотреть на всю картину в целом.
Например, есть сервис, который получает данные от API и сохраняет их в базу. Проверим, как это работает:
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Services\ExternalApiService;
class ApiIntegrationTest extends TestCase
{
use RefreshDatabase;
public function test_external_api_data_is_saved_to_database()
{
$this->mock(ExternalApiService::class, function ($mock) {
$mock->shouldReceive('fetchData')
->once()
->andReturn([
'name' => 'Test Item',
'price' => 100
]);
});
$response = $this->get('/api/fetch-data');
$response->assertStatus(200);
$this->assertDatabaseHas('items', [
'name' => 'Test Item',
'price' => 100
]);
}
}
Здесь мы имитируем то, что наш API вернул данные, и проверяем, что они сохранились в базе.
Функциональные тесты
Функциональные тесты — это взгляд на приложения глазами пользователя. Тут пригодится Laravel Dusk — инструмент для браузерных тестов.
Допустим, есть форма входа. Проверим, что она работает корректно:
namespace Tests\Browser;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class LoginTest extends DuskTestCase
{
public function test_user_can_login()
{
$this->browse(function (Browser $browser) {
$browser->visit('/login')
->type('email', 'test@example.com')
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}
Этот тест имитирует реального пользователя: он заходит на страницу логина, вводит email и пароль, а затем проверяет, что после успешного входа его перенаправляют на страницу /home.
Тестирование API
Если есть API его нужно его тестировать, то ларавелл умеет проверять JSON-запросы и ответы. Вот, например, как мы проверим список продуктов:
namespace Tests\Feature;
use Tests\TestCase;
class ProductsApiTest extends TestCase
{
public function test_get_products()
{
$response = $this->getJson('/api/products');
$response->assertStatus(200)
->assertJsonStructure([
'*' => ['id', 'name', 'price']
]);
}
}
Этот тест проверяет, что API возвращает список продуктов с нужными полями.
Примеры использования
Тестирование отправки письма при успешной регистрации
Следующий пример — это классическая ситуация, когда при регистрации нового пользователя система должна отправить приветственное письмо. Нужно убедиться, что оно действительно отправляется.
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
class WelcomeEmailTest extends TestCase
{
use RefreshDatabase;
public function test_welcome_email_is_sent_upon_registration()
{
Mail::fake();
// Выполняем регистрацию пользователя
$response = $this->post('/register', [
'name' => 'Ivan Ivanov',
'email' => 'ivan@example.com',
'password' => 'password123',
'password_confirmation' => 'password123'
]);
// Проверяем, что пользователь успешно создан
$this->assertDatabaseHas('users', ['email' => 'ivan@example.com']);
// Проверяем, что письмо было отправлено
Mail::assertSent(WelcomeEmail::class, function ($mail) {
return $mail->hasTo('ivan@example.com');
});
}
}
Тест позволит убедиться, что система действительно отправляет email каждому новому пользователю.
Тестирование удаления записей из базы данных
Предположим, есть CRUD-система для управления записями. Нужно убедиться, что записи корректно удаляются, а также правильно обрабатываются сопутствующие данные. Протестируем сценарий удаления записи и проверим, что данные действительно ушли в никуда.
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Models\Post;
class PostDeletionTest extends TestCase
{
use RefreshDatabase;
public function test_post_can_be_deleted()
{
// Создаем пост
$post = Post::factory()->create();
// Отправляем запрос на удаление
$response = $this->delete('/posts/' . $post->id);
// Проверяем успешный статус и редирект
$response->assertStatus(302);
$response->assertRedirect('/posts');
// Проверяем, что запись удалена из базы
$this->assertDatabaseMissing('posts', ['id' => $post->id]);
}
}
Этот тест проверяет:
Успешное удаление записи.
Отсутствие записи в базе данных после удаления.
Тест защитит от сценариев, когда запись вроде бы удаляется, но остаётся в базе.
Всех желающих приглашаем на открытый урок «Использование GraphQL в Laravel проектах». На нем сравним RESTFul и Graphql; предоставим API, используя Graphql; а также посмотрим, как работать с Graphql на фронтэнде. Записаться на урок можно на странице курса "Framework Laravel".