Мокирование в тестах Laravel

PhpUnit

Мокирование — это мощный инструмент для изоляции тестируемого кода от его зависимостей. В Laravel и PHPUnit вы можете мокать практически всё, что угодно: фасады, модели, репозитории, сервисы, HTTP-клиенты и даже глобальные функции. Давайте разберем, что и как можно мокать.


Что можно мокать?

1. Фасады (Facades)

Фасады Laravel (например, DB, Cache, Config, Storage) легко мокать с помощью shouldReceive.

Пример:

use Illuminate\Support\Facades\DB;

DB::shouldReceive('select')
    ->once()
    ->withAnyArgs()
    ->andReturn([
        (object) ['UF_PROJECT' => 384, 'RATING' => '5.25']
    ]);

2. Модели (Eloquent Models)

Модели Eloquent можно мокать, чтобы имитировать их поведение. Например, вы можете мокать методы where, find, create и другие.

Пример:

use App\Models\User;
use Mockery;

$userMock = Mockery::mock(User::class);
$userMock->shouldReceive('find')
    ->once()
    ->with(1)
    ->andReturn((object) ['id' => 1, 'name' => 'John Doe']);

// Теперь $userMock будет возвращать фиктивные данные при вызове find(1)

3. Репозитории

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

Пример:

use App\Repositories\UserRepository;
use Mockery;

$repositoryMock = Mockery::mock(UserRepository::class);
$repositoryMock->shouldReceive('getUserById')
    ->once()
    ->with(1)
    ->andReturn((object) ['id' => 1, 'name' => 'John Doe']);

// Теперь $repositoryMock будет возвращать фиктивные данные при вызове getUserById(1)

4. Сервисы

Сервисы, которые зависят от других сервисов или репозиториев, также можно мокать.

Пример:

use App\Services\UserService;
use Mockery;

$userServiceMock = Mockery::mock(UserService::class);
$userServiceMock->shouldReceive('getUserDetails')
    ->once()
    ->with(1)
    ->andReturn(['id' => 1, 'name' => 'John Doe']);

// Теперь $userServiceMock будет возвращать фиктивные данные при вызове getUserDetails(1)

5. HTTP-клиенты

Если ваш код делает HTTP-запросы (например, через Guzzle или Laravel HTTP-клиент), вы можете мокать эти запросы.

Пример:

use Illuminate\Support\Facades\Http;

Http::fake([
    'api.example.com/users/1' => Http::response(['id' => 1, 'name' => 'John Doe'], 200),
]);

// Теперь все запросы к api.example.com/users/1 будут возвращать фиктивные данные

6. Глобальные функции

Если ваш код использует глобальные функции (например, time(), file_get_contents), их также можно мокать с помощью библиотеки Mockery.

Пример:

use Mockery;

Mockery::mock('alias:time')->andReturn(1234567890);

// Теперь time() всегда будет возвращать 1234567890

7. События (Events)

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

Пример:

use Illuminate\Support\Facades\Event;

Event::fake();

// Ваш код, который отправляет событие
event(new UserRegistered($user));

// Проверяем, что событие было отправлено
Event::assertDispatched(UserRegistered::class);

8. Уведомления (Notifications)

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

Пример:

use Illuminate\Support\Facades\Notification;

Notification::fake();

// Ваш код, который отправляет уведомление
$user->notify(new WelcomeNotification);

// Проверяем, что уведомление было отправлено
Notification::assertSentTo($user, WelcomeNotification::class);

9. Очереди (Queues)

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

Пример:

use Illuminate\Support\Facades\Queue;

Queue::fake();

// Ваш код, который добавляет задачу в очередь
dispatch(new ProcessPodcast($podcast));

// Проверяем, что задача была добавлена в очередь
Queue::assertPushed(ProcessPodcast::class);

10. Почта (Mail)

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

Пример:

use Illuminate\Support\Facades\Mail;

Mail::fake();

// Ваш код, который отправляет письмо
Mail::to('user@example.com')->send(new WelcomeMail);

// Проверяем, что письмо было отправлено
Mail::assertSent(WelcomeMail::class);

Как мокать?

1. Использование Mockery

Mockery — это мощная библиотека для создания мок-объектов. Она интегрирована с PHPUnit и Laravel.

Пример:

use Mockery;

$mock = Mockery::mock(ClassName::class);
$mock->shouldReceive('methodName')
    ->once()
    ->with('arg1', 'arg2')
    ->andReturn('result');

2. Использование фасадов

Фасады Laravel можно мокать с помощью shouldReceive.

Пример:

use Illuminate\Support\Facades\DB;

DB::shouldReceive('select')
    ->once()
    ->withAnyArgs()
    ->andReturn([/* фиктивные данные */]);

3. Использование PHPUnit

PHPUnit также предоставляет встроенные методы для создания мок-объектов.

Пример:

$mock = $this->createMock(ClassName::class);
$mock->method('methodName')
    ->willReturn('result');

Когда мокать?

  • Изоляция тестов: Моки позволяют изолировать тестируемый код от его зависимостей.
  • Тестирование исключений: Вы можете мокать методы, чтобы они выбрасывали исключения.
  • Тестирование сложных сценариев: Моки позволяют имитировать сложные сценарии, которые трудно воспроизвести в реальных условиях.

Итог

Вы можете мокать практически всё, что угодно: фасады, модели, репозитории, сервисы, HTTP-клиенты, глобальные функции, события, уведомления, очереди и почту. Это делает ваши тесты более гибкими и изолированными от внешних зависимостей.

Теперь вы знаете, как мокать различные части вашего приложения! 🚀