How To Test Mail and Database Notifications in Laravel 8
I was working on a project in Laravel and needed to test the mail content of a mail notification and also the database notification but I could not figure it out. After some research, I figured that you can actually unit test or assert mail notification content as well as database notification without sending an actual notification. I will show you how to do that in this article.
If you are reading this, I will assume that you already know how to write unit tests in Laravel using PHPUnit. If you don't , you can click here to get you started.
Firstly, we will create a notification using this Laravel command
php artisan make:notification TestNotification
Your file should look like this.
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class TestNotification extends Notification
{
use Queueable;
private $user;
public function __construct($user)
{
$this->$user = $user;
}
public function via($notifiable)
{
return ['mail','database'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
public function toArray($notifiable)
{
return [
'some' => 'data'
];
}
}
And then in your test, you should fake the notification by calling the notification facade and then a static function fake().
We are going to test the database notification. In your test file, create a function testDatabaseNotification(). Afterward, we will arrange our test case, act and assert.
public function testDatabaseNotification()
{ // Arrange
Notification::fake();
$this->user = User::factory()->create();
$email_subject = "Test Notification";
// Act
$this->user->notify(new TestNotification($email_subject));
// Assert
Notification::assertSentTo($this->user, TestNotification::class, function ($notification, $channels) use($email_subject){
$this->assertContains('database', $channels);
$databaseNotification = (object)$notification->toArray($this->user);
$this->assertEquals( $email_subject, $databaseNotification->title);
return true;
});
}
Now we are going to test our mail notification
public function testMailNotification()
{ // Arrange
Notification::fake();
$this->user = User::factory()->create();
$email_subject = "Test notification";
// Act
$this->user->notify(new TestNotification($email_subject));
// Assert
Notification::assertSentTo($this->user, TestNotification::class, function ($notification, $channels) use($email_subject){
$this->assertContains('mail', $channels);
$mailNotification = (object)$notification->toMail($this->user);
$this->assertEquals($email_subject, $mailNotification->subject,);
$this->assertEquals('The introduction to the notification.', $mailNotification->introLines[0]);
$this->assertEquals('Thank you for using our application!', $mailNotification->outroLines[0]);
$this->assertEquals('Notification Action', $mailNotification->actionText);
$this->assertEquals($mailNotification->actionUrl, url('/'));
return true;
});
}
You see here that we are retrieving the content of the mail notification.
Here is the full code
<?php
namespace Tests\Unit;
use App\Notifications\TestNotification;
use Illuminate\Support\Facades\Notification;
use App\Model\User;
use Tests\TestCase;
class MailNotificationTest extends TestCase
{
/**
* @var \Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Model|mixed
*/
private $user;
/**
* A basic test example.
*
* @return void
*/
public function testDatabaseNotification()
{
Notification::fake();
$this->user = User::factory()->create();
$email_subject = "Test Notification";
$this->user->notify(new TestNotification($email_subject));
Notification::assertSentTo($this->user, TestNotification::class, function ($notification, $channels) use($email_subject){
$this->assertContains('database', $channels);
$databaseNotification = (object)$notification->toArray($this->user);
$this->assertEquals( $email_subject, $databaseNotification->title);
return true;
});
}
public function testMailNotification()
{
Notification::fake();
$this->user = User::factory()->create();
$email_subject = "Test notification";
$this->user->notify(new TestNotification($email_subject));
Notification::assertSentTo($this->user, TestNotification::class, function ($notification, $channels) use($email_subject){
$this->assertContains('mail', $channels);
$mailNotification = (object)$notification->toMail($this->user);
$this->assertEquals($email_subject, $mailNotification->subject,);
$this->assertEquals('The introduction to the notification.', $mailNotification->introLines[0]);
$this->assertEquals('Thank you for using our application!', $mailNotification->outroLines[0]);
$this->assertEquals('Notification Action', $mailNotification->actionText);
$this->assertEquals($mailNotification->actionUrl, url('/'));
return true;
});
}
}
Now we have successfully asserted mail and database content in Laravel notification.
Please share in the comment below what you think about this.
Happy Coding :)