イントロダクションIntroduction
Laravelアプリケーションをテストするとき、アプリケーションの一部分を「モック」し、特定のテストを行う間は実際のコードを実行したくない場合があります。たとえば、イベントを発行するコントローラをテストする時は、実際に実行したくないイベントリスナをモックしたいと思うことでしょう。これにより、コントローラのHTTPレスポンスだけをテストでき、イベントリスナの実行は心配しなくて済みます。なぜなら、イベントリスナは自身のテストケースにおいて、テストできるからです。When testing Laravel applications, you may wish to "mock" certain aspects of your application so they are not actually executed during a given test. For example, when testing a controller that dispatches an event, you may wish to mock the event listeners so they are not actually executed during the test. This allows you to only test the controller's HTTP response without worrying about the execution of the event listeners, since the event listeners can be tested in their own test case.
Laravelにはイベント、ジョブ、ファサードを最初からモックできるヘルパが準備されています。これらのヘルパは主にMockery上で動作する便利なレイヤーを提供しているので、複雑なMockeryのメソッドコールを自分で作成する必要はありません。MockeryやPHPUnitを使用し、自身のモックやスパイを自由に作成してください。Laravel provides helpers for mocking events, jobs, and facades out of the box. These helpers primarily provide a convenience layer over Mockery so you do not have to manually make complicated Mockery method calls. You can also use Mockery[http://docs.mockery.io/en/latest/] or PHPUnit to create your own mocks or spies.
オブジェクトのモックMocking Objects
Laravelのサービスコンテナにより、アプリケーションへ依存注入されるオブジェクトをモックする場合は、モックしたインスタンスをコンテナへ、instance
結合する必要があります。これによりコンテナへ対象のオブジェクトそのものを構築する代わりに、モックしたインスタンスオブジェクトを使用するように指示します。When mocking an object that is going to be injected into your application via Laravel's service container, you will need to bind your mocked instance into the container as an instance
binding. This will instruct the container to use your mocked instance of the object instead of constructing the object itself:
use App\Service;
use Mockery;
$this->instance(Service::class, Mockery::mock(Service::class, function ($mock) {
$mock->shouldReceive('process')->once();
}));
これをより便利にするため、Laravelのベーステストケースクラスでは、mock
メソッドが使用できます。In order to make this more convenient, you may use the mock
method, which is provided by Laravel's base test case class:
use App\Service;
$this->mock(Service::class, function ($mock) {
$mock->shouldReceive('process')->once();
});
オブジェクトのいくつかのメソッドだけをモックする必要があるなら、partialMock
メソッドが使えます。モックしていないメソッドは、呼び出し時に通常通り実行されます。You may use the partialMock
method when you only need to mock a few methods of an object. The methods that are not mocked will be executed normally when called:
use App\Service;
$this->partialMock(Service::class, function ($mock) {
$mock->shouldReceive('process')->once();
});
同様に、オブジェクトをスパイしたい場合は、Laravelの便利なMockery::spy
ラッパーであり、ベースのテストケースクラスで提供しているspy
メソッドを用います。Similarly, if you want to spy on an object, Laravel's base test case class offers a spy
method as a convenient wrapper around the Mockery::spy
method:
use App\Service;
$this->spy(Service::class, function ($mock) {
$mock->shouldHaveReceived('process');
});
Bus FakeBus Fake
モックの別の方法は、Bus
ファサードのfake
メソッドを使用し、ジョブがディスパッチされないようにすることです。fakeを使用する場合、アサートはテスト下のコードが終了した時点で行われます。As an alternative to mocking, you may use the Bus
facade's fake
method to prevent jobs from being dispatched. When using fakes, assertions are made after the code under test is executed:
<?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();
// 注文の実行コード…
Bus::assertDispatched(ShipOrder::class, function ($job) use ($order) {
return $job->order->id === $order->id;
});
// ジョブがディスパッチされないことを宣言
Bus::assertNotDispatched(AnotherJob::class);
}
}
Event FakeEvent Fake
モックの別の方法は、Event
ファサードのfake
メソッドを使用し、全イベントリスナが実行されないようにすることです。その後で、イベントがディスパッチされたことをアサートし、さらに受け取ったデータの検査もできます。fakeを使用する場合、アサートはテストを実施したコードの後に実行されます。As an alternative to mocking, you may use the Event
facade's fake
method to prevent all event listeners from executing. You may then assert that events were dispatched and even inspect the data they received. When using fakes, assertions are made after the code under test is executed:
<?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
{
/**
* 注文発送のテスト
*/
public function testOrderShipping()
{
Event::fake();
// 注文の実行コード…
Event::assertDispatched(OrderShipped::class, function ($e) use ($order) {
return $e->order->id === $order->id;
});
// イベントが2回ディスパッチされることをアサート
Event::assertDispatched(OrderShipped::class, 2);
// イベントがディスパッチされないことをアサート
Event::assertNotDispatched(OrderFailedToShip::class);
}
}
Note:
Event::fake()
を呼び出した後は、イベントリスナは実行されなくなります。そのためたとえば、モデルのcreating
イベントでUUIDを生成するなど、イベントに結びつけたモデルファクトリの使用をテストする場合は、ファクトリを呼び出した後に、Event::fake()
を呼び出す必要があります。{note} After callingEvent::fake()
, no event listeners will be executed. So, if your tests use model factories that rely on events, such as creating a UUID during a model'screating
event, you should callEvent::fake()
after using your factories.
イベントのサブセットのFakeFaking A Subset Of Events
特定のイベントに対してだけ、イベントリスナをフェイクしたい場合は、fake
かfakeFor
メソッドに指定してください。If you only want to fake event listeners for a specific set of events, you may pass them to the fake
or fakeFor
method:
/**
* 受注処理のテスト
*/
public function testOrderProcess()
{
Event::fake([
OrderCreated::class,
]);
$order = factory(Order::class)->create();
Event::assertDispatched(OrderCreated::class);
// 他のイベントは通常通りにディスパッチされる
$order->update([...]);
}
限定的なEvent FakeScoped Event Fakes
テストの一部分だけでイベントをフェイクしたい場合は、fakeFor
メソッドを使用します。If you only want to fake event listeners for a portion of your test, you may use the fakeFor
method:
<?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
{
/**
* 注文発送のテスト
*/
public function testOrderProcess()
{
$order = Event::fakeFor(function () {
$order = factory(Order::class)->create();
Event::assertDispatched(OrderCreated::class);
return $order;
});
// イベントは通常通りにディスパッチされ、オブザーバが実行される
$order->update([...]);
}
}
Mail FakeMail Fake
Mail
ファサードのfake
メソッドを使い、メールが送信されるのを防ぐことができます。その後で、Mailableがユーザーへ送信されたかをアサートし、受け取ったデータを調べることさえできます。Fakeを使用する場合、テスト対象のコードが実行された後で、アサートしてください。You may use the Mail
facade's fake
method to prevent mail from being sent. You may then assert that mailables[/docs/{{version}}/mail] were sent to users and even inspect the data they received. When using fakes, assertions are made after the code under test is executed:
<?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();
// 注文の実行コード…
Mail::assertSent(OrderShipped::class, function ($mail) use ($order) {
return $mail->order->id === $order->id;
});
// メッセージが指定したユーザーに届いたことをアサート
Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) &&
$mail->hasCc('...') &&
$mail->hasBcc('...');
});
// mailableが2回送信されたことをアサート
Mail::assertSent(OrderShipped::class, 2);
// mailableが送られなかったことをアサート
Mail::assertNotSent(AnotherMailable::class);
}
}
バックグランドで送信するために、mailableをキュー投入している場合は、assertSent
の代わりにassertQueued
メソッドを使用してください。If you are queueing mailables for delivery in the background, you should use the assertQueued
method instead of assertSent
:
Mail::assertQueued(...);
Mail::assertNotQueued(...);
Notification FakeNotification Fake
Notification
ファサードのfake
メソッドを使用し、通知が送られるのを防ぐことができます。その後で、通知がユーザーへ送られたことをアサートし、受け取ったデータを調べることさえできます。Fakeを使用するときは、テスト対象のコードが実行された後で、アサートを作成してください。You may use the Notification
facade's fake
method to prevent notifications from being sent. You may then assert that notifications[/docs/{{version}}/notifications] were sent to users and even inspect the data they received. When using fakes, assertions are made after the code under test is executed:
<?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();
// 注文の実行コード…
Notification::assertSentTo(
$user,
OrderShipped::class,
function ($notification, $channels) use ($order) {
return $notification->order->id === $order->id;
}
);
// 通知が指定したユーザーへ送られたことをアサート
Notification::assertSentTo(
[$user], OrderShipped::class
);
// 通知が送られなかったことをアサート
Notification::assertNotSentTo(
[$user], AnotherNotification::class
);
// 通知がNotification::route()メソッドにより送られたことをアサート
Notification::assertSentTo(
new AnonymousNotifiable, OrderShipped::class
);
// Notification::route()メソッドで通知を現在のユーザーに送ったことをアサート
Notification::assertSentTo(
new AnonymousNotifiable,
OrderShipped::class,
function ($notification, $channels, $notifiable) use ($user) {
return $notifiable->routes['mail'] === $user->email;
}
);
}
}
Queue FakeQueue Fake
モックの代替として、Queue
ファサードのfake
メソッドを使い、ジョブがキューされるのを防ぐことができます。その後で、ジョブがキューへ投入されたことをアサートし、受け取ったデータの内容を調べることもできます。Fakeを使う場合は、テスト対象のコードを実行したあとで、アサートしてください。As an alternative to mocking, you may use the Queue
facade's fake
method to prevent jobs from being queued. You may then assert that jobs were pushed to the queue and even inspect the data they received. When using fakes, assertions are made after the code under test is executed:
<?php
namespace Tests\Feature;
use App\Jobs\AnotherJob;
use App\Jobs\FinalJob;
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();
// ジョブが投入されていないことをアサート
Queue::assertNothingPushed();
// 注文の実行コード…
Queue::assertPushed(ShipOrder::class, function ($job) use ($order) {
return $job->order->id === $order->id;
});
// 特定のキューへジョブが投入されたことをアサート
Queue::assertPushedOn('queue-name', ShipOrder::class);
// ジョブが2回投入されたことをアサート
Queue::assertPushed(ShipOrder::class, 2);
// ジョブが投入されなかったことをアサート
Queue::assertNotPushed(AnotherJob::class);
// ジョブが指定したジョブチェーンで投入され、クラスが一致していることをアサート
Queue::assertPushedWithChain(ShipOrder::class, [
AnotherJob::class,
FinalJob::class
]);
// ジョブが指定したジョブチェーンで投入され、クラスとプロパティ両方が一致していることをアサート
Queue::assertPushedWithChain(ShipOrder::class, [
new AnotherJob('foo'),
new FinalJob('bar'),
]);
// ジョブチェーンを使わずに、ジョブが投入されたことをアサート
Queue::assertPushedWithoutChain(ShipOrder::class);
}
}
Storage FakeStorage Fake
とてもシンプルにファイルアップロードのテストを行うため、Storage
ファサードのfake
メソッドにより、UploadedFile
クラスのファイル生成ユーティリティと組み合わされたフェイクディスクを簡単に生成できます。The Storage
facade's fake
method allows you to easily generate a fake disk that, combined with the file generation utilities of the UploadedFile
class, greatly simplifies the testing of file uploads. For example:
<?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')
]);
// ひとつ以上のファイルが保存されたことをアサート
Storage::disk('photos')->assertExists('photo1.jpg');
Storage::disk('photos')->assertExists(['photo1.jpg', 'photo2.jpg']);
// ひとつ以上のファイルが保存されなかったことをアサート
Storage::disk('photos')->assertMissing('missing.jpg');
Storage::disk('photos')->assertMissing(['missing.jpg', 'non-existing.jpg']);
}
}
">Tip!!
fake
メソッドはデフォルトとして、一時ディレクトリ内の全ファイルを削除します。ファイルを残しておきたい場合は、代わりにpersistentFake
メソッドを使用してください。{tip} By default, thefake
method will delete all files in its temporary directory. If you would like to keep these files, you may use the "persistentFake" method instead.
ファサードFacades
伝統的な静的メソッドの呼び出しと異なり、ファサードはモックできます。これにより伝統的な静的メソッドより遥かなアドバンテージを得られ、依存注入を使用する場合と同じテスタビリティを持てます。テスト時は、コントローラのLaravelファサード呼び出しを頻繁にモックしたくなります。例として、以下のようなコントローラアクションを考えてください。Unlike traditional static method calls, facades[/docs/{{version}}/facades] may be mocked. This provides a great advantage over traditional static methods and grants you the same testability you would have if you were using dependency injection. When testing, you may often want to mock a call to a Laravel facade in one of your controllers. For example, consider the following controller action:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* アプリケーションの全ユーザーリストの表示
*
* @return Response
*/
public function index()
{
$value = Cache::get('key');
//
}
}
shouldReceive
メソッドを使用し、Cache
ファサードへの呼び出しをモックできます。これはMockeryインスタンスを返します。ファサードはLaravelのサービスコンテナにより管理され、依存解決されていますので、典型的な静的クラスよりもかなり高いテスタビリティーを持っています。例としてCache
ファサードへのget
メソッド呼び出しをモックしてみましょう。We can mock the call to the Cache
facade by using the shouldReceive
method, which will return an instance of a Mockery[https://github.com/padraic/mockery] mock. Since facades are actually resolved and managed by the Laravel service container[/docs/{{version}}/container], they have much more testability than a typical static class. For example, let's mock our call to the Cache
facade's get
method:
<?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');
// ...
}
}
Note:
Request
ファサードをモックしてはいけません。代わりに、テスト実行時はget
やpost
のようなHTTPヘルパメソッドへ、望む入力を引数として渡してください。同様に、Config
ファサードはモックを使う代わりに、テストではConfig::set
メソッドを呼び出してください。{note} You should not mock theRequest
facade. Instead, pass the input you desire into the HTTP helper methods such asget
andpost
when running your test. Likewise, instead of mocking theConfig
facade, call theConfig::set
method in your tests.