Имитация
Содержание:
Введение
При тестировании приложений Laravel, вы можете захотеть "сымитировать" некоторые аспекты
вашего приложения так, чтобы они фактически не выполнялись во время проведения данного теста.
Например, при тестировании контроллера, который запускает событие, вы можете захотеть
сымитироваться действия слушателей события, чтобы они не запускались во время теста.
Это позволит вам протестировать HTTP ответ контроллера без необходимости запуска
слушателей события, в силу того, что они могут быть протестированы отдельно.
Ларавел предоставляет помощники для имитации событий, заданий и фасадов из коробки.
Эти помощники предоставляют убодный слой-надстройку над базовыми методами так,
что вам не необходимости состовлять методы имитации полностью.
Вы также можете использовать Mockery или PHPUnit
для создания ваших собственных имитаций или агентов.
Имитация объектов
При имитации объекта, который должен быть внедрён в ваше приложение через сервис контейнер
Ларавел, вам необходимо связать ваши имитируемые экземпляры в контейнер через instance
.
Это научит контейнер использовать имитируемый объект вместо самого объекта:
use App\Service;
use Mockery;
$this->instance(Service::class, Mockery::mock(Service::class, function ($mock) {
$mock->shouldReceive('process')->once();
}));
Чтобы упростить работу с такого рода, вы можете использовать метод mock
,
который предоставлен в базовов случае тестового класса Ларавел:
use App\Service;
$this->mock(Service::class, function ($mock) {
$mock->shouldReceive('process')->once();
});
Похожим образом вы можете отслеживать объект. В базовов случае Ларавел предлагает
метод spy
в качестве удобной обёртки для метода Mockery::spy
:
use App\Service;
$this->spy(Service::class, function ($mock) {
$mock->shouldHaveReceived('process');
});
Фасад Bus
В качестве альтернативы имитации, вы можете использовать метод fake
для фасада Bus
, чтобы предотвратить выполнение задания.
При использовании фейков, предположения делаются под кодом после выполнения теста:
<?php
namespace Tests\Feature;
use App\Jobs\ShipOrder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Bus;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function testOrderShipping()
{
Bus::fake();
// Perform order shipping...
Bus::assertDispatched(ShipOrder::class, function ($job) use ($order) {
return $job->order->id === $order->id;
});
// Assert a job was not dispatched...
Bus::assertNotDispatched(AnotherJob::class);
}
}
Фальшивое событие
Как альтернатива для имитации, вы можете использовать метод fake
для фасада Event
для предотвращения выполнения слушателей события.
Вы можете потом утвердить, что событие было запущено и даже проверить полученные данные.
При использовании фейков, утверждения делаются под кодом после выполнения теста:
<?php
namespace Tests\Feature;
use App\Events\OrderFailedToShip;
use App\Events\OrderShipped;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* Test order shipping.
*/
public function testOrderShipping()
{
Event::fake();
// Perform order shipping...
Event::assertDispatched(OrderShipped::class, function ($e) use ($order) {
return $e->order->id === $order->id;
});
// Assert an event was dispatched twice...
Event::assertDispatched(OrderShipped::class, 2);
// Assert an event was not dispatched...
Event::assertNotDispatched(OrderFailedToShip::class);
}
}
После выполнения Event::fake()
ни один слушатель не будет запущен.
Поэтому, если вы используете фабрики моделей, которые опираются на события,
такие как создание UUID во время создания creating
события модели,
то вам необходимо вызвать Event::fake()
после использования
ваших фабрик.
Подделка подмножества событий
Если вы хотите подделать слушателей для определённого набора событий,
вы можете передать их в метод fake
или fakeFor
:
/**
* Test order process.
*/
public function testOrderProcess()
{
Event::fake([
OrderCreated::class,
]);
$order = factory(Order::class)->create();
Event::assertDispatched(OrderCreated::class);
// Other events are dispatched as normal...
$order->update([...]);
}
Фальшивое событие слушателю
>
Если вы хотите подделать слушателей события для определённой части вашего теста,
вы можете использовать метод fakeFor
:
<?php
namespace Tests\Feature;
use App\Events\OrderCreated;
use App\Order;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* Test order process.
*/
public function testOrderProcess()
{
$order = Event::fakeFor(function () {
$order = factory(Order::class)->create();
Event::assertDispatched(OrderCreated::class);
return $order;
});
// Events are dispatched as normal and observers will run ...
$order->update([...]);
}
}
Фальшивая почта
Вы можете использовать метод fake
для фасада Mail
,
чтобы предотвратить отправку почты.
Вы можете утвердить после, что почта отправлена пользователям и даже проверить полученные данные.
При использовании фейков, утверждения делаются под кодом после выполнения теста:
<?php
namespace Tests\Feature;
use App\Mail\OrderShipped;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function testOrderShipping()
{
Mail::fake();
// Assert that no mailables were sent...
Mail::assertNothingSent();
// Perform order shipping...
Mail::assertSent(OrderShipped::class, function ($mail) use ($order) {
return $mail->order->id === $order->id;
});
// Assert a message was sent to the given users...
Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) &&
$mail->hasCc('...') &&
$mail->hasBcc('...');
});
// Assert a mailable was sent twice...
Mail::assertSent(OrderShipped::class, 2);
// Assert a mailable was not sent...
Mail::assertNotSent(AnotherMailable::class);
}
}
Если вы используете очереди для отправки почты, в этом случае необходимо
использовать метод assertQueued
вместо assertSent
:
Mail::assertQueued(...);
Mail::assertNotQueued(...);
Фальшивые уведомления
Вы можете использовать метод fake
для фасада Notification
для предотвращения отправки уведомлений. Вы можете после предположить,
что уведомления были отправлены пользователям и даже проверить полученные данные.
При использовании фальшивых уведомлений, утверждения делаются под кодом после выполнения теста:
<?php
namespace Tests\Feature;
use App\Notifications\OrderShipped;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Notifications\AnonymousNotifiable;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function testOrderShipping()
{
Notification::fake();
// Assert that no notifications were sent...
Notification::assertNothingSent();
// Perform order shipping...
Notification::assertSentTo(
$user,
OrderShipped::class,
function ($notification, $channels) use ($order) {
return $notification->order->id === $order->id;
}
);
// Assert a notification was sent to the given users...
Notification::assertSentTo(
[$user], OrderShipped::class
);
// Assert a notification was not sent...
Notification::assertNotSentTo(
[$user], AnotherNotification::class
);
// Assert a notification was sent via Notification::route() method...
Notification::assertSentTo(
new AnonymousNotifiable, OrderShipped::class
);
}
}
Фальшивая очередь
Как альтернатива имитации, вы можете исопльзовать метод fake
для фасада Queue
, чтобы предотвратить добавления задания в очередь.
Вы можете утверждать, что задание были добавлены в очередь и даже проверить полученные данные.
При использовании фейков, утверждения делаются под кодом после выполнения теста:
<?php
namespace Tests\Feature;
use App\Jobs\ShipOrder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function testOrderShipping()
{
Queue::fake();
// Assert that no jobs were pushed...
Queue::assertNothingPushed();
// Perform order shipping...
Queue::assertPushed(ShipOrder::class, function ($job) use ($order) {
return $job->order->id === $order->id;
});
// Assert a job was pushed to a given queue...
Queue::assertPushedOn('queue-name', ShipOrder::class);
// Assert a job was pushed twice...
Queue::assertPushed(ShipOrder::class, 2);
// Assert a job was not pushed...
Queue::assertNotPushed(AnotherJob::class);
// Assert a job was pushed with a specific chain...
Queue::assertPushedWithChain(ShipOrder::class, [
AnotherJob::class,
FinalJob::class
]);
}
}
Фальшивое хранилище
Метод fake
для фасада Storage
позволяет вам легко сгенерировать
фальшивый диск, который, в сочетании с возможностью генерации файла класса UploadedFile
,
существенно упрощает тестирование загрузки файла. Например:
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function testAlbumUpload()
{
Storage::fake('photos');
$response = $this->json('POST', '/photos', [
UploadedFile::fake()->image('photo1.jpg'),
UploadedFile::fake()->image('photo2.jpg')
]);
// Assert one or more files were stored...
Storage::disk('photos')->assertExists('photo1.jpg');
Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
// Assert one or more files were not stored...
Storage::disk('photos')->assertMissing('missing.jpg');
Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
}
}
По умолчанию, метод fake
удалить все файлы во временной директории.
Если вы хотите удержать эти файлы, вы можете использовать метод persistentFake
вместо.
Имитация фасадов
В противоположность традиционным вызовам статичных методов, фасады можно имировать.
Это дает огромное преимущество над традиционными статичными методам и предоставляет вам
такой же уровень тестируемости, как и при использовании внедрения зависимости.
При тестировании, вы часто можете захотеть сымитировать вызов фасада в одном из ваших контролллеров.
Например, представим следующее действие контроллера:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* Show a list of all users of the application.
*
* @return Response
*/
public function index()
{
$value = Cache::get('key');
//
}
}
Мы можем имитировать вызов фасада Cache
используя метод shouldReceive
,
который вернёт экземпляр Mockery фальшивки.
Фасады имеют лучшую тестируемость чем обычный статичный класс в силу того,
что поключены и функционируют через сервис контейнер.
Для примера, давайте сымитируем вызов метода get
для фасада Cache
:
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;
class UserControllerTest extends TestCase
{
public function testGetIndex()
{
Cache::shouldReceive('get')
->once()
->with('key')
->andReturn('value');
$response = $this->get('/users');
// ...
}
}
Вам не следует имитировать фасад Request
. Вместо этого,
передайте некоторые вводные данные в HTTP методы-помощники такие как
get
и post
при выполнении вашего теста.
Таким же образом, вместо имитации фасада Config
,
вызовите метод Config::set
в ваших тестах.