How to test email message from notifications in Laravel (MailMessage).
<?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 [
//
];
}
}
// 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);
}
<?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;
});
}
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;
// }
}
}
<?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();
});
*/