Back to Course |
Testing in Laravel 11: Advanced Level

Testing Emails and Notifications with Fake

How do you test sending emails? In Laravel, the process of sending emails can be "faked."


Scenario Example

We continue the same topic from the previous lessons. You send mail or notifications to the admin user within a Job, dispatched from the Controller or anywhere else.

use App\Models\User;
use App\Models\Product;
use App\Mail\NewProductCreatedMail;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use App\Notifications\NewProductCreatedNotification;
 
class NewProductNotifyJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 
public function __construct(private readonly Product $product)
{}
 
public function handle(): void
{
$admin = User::where('is_admin', true)->first();
 
Mail::to($admin->email)->send(new NewProductCreatedMail($this->product));
 
Notification::send($admin, new NewProductCreatedNotification($this->product));
}
}

Both mail and notifications are only created using an Artisan command, and nothing changes inside them.


The Test

Again, we start the test by making a request to create a product.

use App\Models\User;
use App\Jobs\NewProductNotifyJob;
use function Pest\Laravel\actingAs;
 
beforeEach(function () {
$this->user = User::factory()->create();
});
 
test('product test create job mail send successfully', function () {
$admin = User::factory()->create(['is_admin' => true]);
 
actingAs($this->user)
->post('/products', [
'name' => 'Product 123',
'price' => 1234,
]);
 
// assert
});

Next, as with any fake, we must use the Mail facade and fake() method.

use App\Models\User;
use App\Jobs\NewProductNotifyJob;
use function Pest\Laravel\actingAs;
use Illuminate\Support\Facades\Mail;
use App\Mail\NewProductCreatedMail;
 
beforeEach(function () {
$this->user = User::factory()->create();
});
 
test('product test create job mail send successfully', function () {
Mail::fake();
 
$admin = User::factory()->create(['is_admin' => true]);
 
actingAs($this->user)
->post('/products', [
'name' => 'Product 123',
'price' => 1234,
]);
 
Mail::assertSent(NewProductCreatedMail::class);
});

If you are using notifications, it's the same: use the Notification facade with the fake() method.

The only difference is that when asserting a notification, you must provide the user who should receive it.

use App\Models\User;
use App\Jobs\NewProductNotifyJob;
use function Pest\Laravel\actingAs;
use Illuminate\Support\Facades\Notification;
use App\Notifications\NewProductCreatedNotification;
 
beforeEach(function () {
$this->user = User::factory()->create();
});
 
test('product test create job mail send successfully', function () {
Mail::fake();
Notification::fake();
 
$admin = User::factory()->create(['is_admin' => true]);
 
actingAs($this->user)
->post('/products', [
'name' => 'Product 123',
'price' => 1234,
]);
 
Mail::assertSent(NewProductCreatedMail::class);
Notification::assertSentTo($admin, NewProductCreatedNotification::class);
});