Skip to content

Instantly share code, notes, and snippets.

@atomjoy
Last active September 8, 2025 11:12
Show Gist options
  • Save atomjoy/9df447f3702353986745d7ab29baa4f9 to your computer and use it in GitHub Desktop.
Save atomjoy/9df447f3702353986745d7ab29baa4f9 to your computer and use it in GitHub Desktop.
Laravel notification email message testing.

Test Email Notifications in Laravel

How to test email message from notifications in Laravel (MailMessage).

Activation Notification

<?php

namespace App\Notifications;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class ActivationEmail extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     */
    public function __construct(protected $hash)
    {
        //
    }

    /**
     * Get the notification's delivery channels.
     *
     * @return array<int, string>
     */
    public function via(object $notifiable): array
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     */
    public function toMail(User $notifiable): MailMessage
    {
        $url = request()->getSchemeAndHttpHost() . '/verify/email/' . $notifiable->getKey() . '/' . $this->hash;

        return (new MailMessage)
            ->subject(__('Email address verification'))
            ->greeting(__('Hello'), $notifiable->name)
            ->line(__('Click the button below to verify email address.'))
            ->action(__('Confirm Email Address'), url($url))
            ->line(__('If you did not create an account, no further action is required.'));
            
        // ->theme('custom')
        // ->view('email.auth.activation', ['url' => $url]);
        // ->theme('auth_' . config('mail.markdown.theme'))
        // ->markdown(email.auth.activation', ['url' => $url]);
    }

    /**
     * Get the array representation of the notification.
     *
     * @return array<string, mixed>
     */
    public function toArray(object $notifiable): array
    {
        return [
            //
        ];
    }
}

Send message

// In controller

$request->validate([
    'name' => 'required|min:3',
    'email' => 'required|email:rfc,dns|unique:users',
    'password' => 'required|min:11|confirmed',
    'password_confirmation' => 'required',
]);

$hash = md5(uniqid() . microtime());

$user = User::create([
    'name' => $request->name,
    'email' => $request->email,
    'password' => Hash::make($request->password),
    'remember_token' => $hash,
    'email_verified_at' => null,
]);
        
if ($user instanceof User) {
    // Send email here
    $user->notifyNow(new ActivationEmail($hash));

    return response()->json([
        'message' => 'Created.',
        // 'hash' => $user->getRememberToken(),
        // 'token' => $user->createToken('api-token')->plainTextToken,
    ], 201);
}

Test

<?php

namespace Tests\Feature;

use App\Models\User;
use App\Notifications\ActivationEmail;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Mail\Events\MessageSent;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;

class NotificationEmailTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Api register user.
     */
    public function test_register_user(): void
    {
        $this->seed();

        $id = uniqid();

        Notification::fake(ActivationEmail::class);

        $response = $this->postJson('/api/register', [
            'name' => 'User' . $id,
            'email' => 'user-' . $id . '@github.com',
            'password' => 'Password123#',
            'password_confirmation' => 'Password123#',
        ]);

        $response->assertStatus(201);

        Notification::assertCount(1);

        $user = User::where('email', 'user-' . $id . '@github.com')->first();

        Notification::assertSentTo([$user], ActivationEmail::class, function ($notification, $channels, $notifiable) {
            // Message
            $body = $notification->toMail($notifiable)->toArray();
            // Action url
            $this->assertTrue(str_contains($body["actionUrl"], $notifiable->getKey() . '/' . $notifiable->remeber_token));
            
            // Subject
            // $this->assertEquals('Welcome to the app', $body['subject']);
            // Channel
            // $this->assertEquals(['mail'], $notification->via($notifiable));
            
            // Ok
            return true;
        });
}

Log email message to file

Does not work when useing Notification::fake() or Mail::fake(), comment first ::fake() lines.

<?php

namespace App\Listeners;

use Illuminate\Mail\Events\MessageSending;
use Illuminate\Support\Facades\Log;

/**
 * Log notifications emails
 * Does not work when useing Notification::fake() or Mail::fake() (comment first ::fake() lines in test file).
 */
class LogNotificationEmail
{
    public function handle(MessageSending $event)
    {
        $message = $event->message;
       
        if (app()->runningUnitTests()) {
            Log::build([
                'driver' => 'single',
                'path' => storage_path('logs/test-emails.log')
            ])->info($message->toString());
        }

        // $event->message->getSubject();
        // $event->message->getBody();
        // $event->message->getHeaders();
        
        // With custom Mailable class check what mailable class used in event
        // if ($event->message->mailable == 'OrderShipped') {
        //    // Do something ...
        //    return false;
        // }
    }
}

Mailable

<?php

namespace App\Mail;

use Illuminate\Mail\Mailable as BaseMailable;

abstract class Mailable extends BaseMailable
{
    /**
     * Send the message using the given mailer.
     *
     * @param  \Illuminate\Contracts\Mail\Factory|\Illuminate\Contracts\Mail\Mailer  $mailer
     * @return \Illuminate\Mail\SentMessage|null
     */
    public function send($mailer)
    {
        // Add varible to mailable message (or put this in mail build() method)
        $this->withSymfonyMessage(function ($message) {
            $message->mailable = get_class($this);
        });

        parent::send($mailer);
    }
}

/*

// In event handler() check what mailable class used and stop it
if ($event->message->mailable == 'OrderShipped') {
    return false;
}

// Symfony\Component\Mime\Email email message class
Event::assertDispatched(MessageSent::class, function ($e) use ($email, $name) {
    $html = $e->message->getHtmlBody();
});
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment