LoginSignup
8
2

More than 5 years have passed since last update.

【Laravel】On-Demand Notifications をモック化してテストする

Posted at

先に結論

次のようにしてできる。

use Illuminate\Notifications\AnonymousNotifiable;

Notification::fake();

// :
// テスト対象のロジック実行
// :

Notification::assertSentTo(
    new AnonymousNotifiable(),
    InvoicePaid::class, // 対象の Notification class
    function($notification, $channels, $notifiable) {
        return $notifiable->routes['mail'] == 'test@example.com';
    }
);

前提1:On-Demand Notifications とは?

メール送信のような「通知」を行う際には通常、 Illuminate\Notifications\Notifiable trait を use した class に対して notify() メソッドを実行する。

On-Demand Notifications はそのような class が存在しない対象に対して、直接通知を行うための仕組みである。

Notification::route('mail', 'taylor@example.com')
            ->route('nexmo', '5555555555')
            ->notify(new InvoicePaid($invoice));

例えば私は、「フォームにメールアドレスだけ入力してもらって、アカウント登録フォームの URL をそのメールアドレスに送信する」という場面で使っている。1

前提2:Notifications をモック化してテスト

テストを実行では、実際にメール送信等はしたくない。
そこで、Laravel では Notifications をモック化する仕組みを提供している。

Notification::fake();

// Perform order shipping...

Notification::assertSentTo(
    $user,
    OrderShipped::class,
    function ($notification, $channels) use ($order) {
        return $notification->order->id === $order->id;
    }
);

このモック化のおかげで、実際にメールを送信することなく「対象にメールが送信されること」を確かめるテストを書くことができる。

本題:On-Demand Notifications をモック化してテスト

しかし、公式ドキュメントの情報からだけでは On-Demand Notifications を使っている場合にモック化する方法がわからない。

Notification::fake();

// こんな風にしていい感じに解釈してくれないかな、と期待したが
Notification::assertSentTo(
    'test@exmaple.com',
    InvoicePaid::class
);
# こんなエラーで怒られる
ErrorException: get_class() expects parameter 1 to be object, string given

/var/www/app/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/NotificationFake.php:165
/var/www/app/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/NotificationFake.php:153
/var/www/app/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/NotificationFake.php:129
/var/www/app/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/NotificationFake.php:43
/var/www/app/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:221
:(以下略)

実は解決方法は Laravel5.5 の時点で既に提示されており、

再掲
use Illuminate\Notifications\AnonymousNotifiable;

Notification::fake();

// :
// テスト対象のロジック実行
// :

Notification::assertSentTo(
    new AnonymousNotifiable(),
    InvoicePaid::class, // 対象の Notification class
    function($notification, $channels, $notifiable) {
        return $notifiable->routes['mail'] == 'test@example.com';
    }
);

のようにすることで、めでたくモック化してテストできる。

余談:AnonymousNotifiable とは?

On-Demand Notifications を利用するときに出てくる Notifialbe な class こそが AnonymousNotifiable

tinker
>>> \Notification::route('mail', 'test@example.com')
=> Illuminate\Notifications\AnonymousNotifiable {
     +routes: [
       "mail" => "test@example.com",
     ],
   }

  1. ちなみに、Laravel 5.7 よりその機能自体が提供されたよう(https://laravel.com/docs/5.7/releases#laravel-5.7) 

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2