Reputation: 1511
I am working on a project using Laravel. I want to use Events and Notifications. For context: A organization can post a Task ( Can be compared to a Job ). A User can apply for that job. As soon as a User applies for a Task an Email should be sent to the contact email stored with the Task.´
My Event: ApplicationCreated
My Notification: ApplicationCreatedNotification
My Listener: SendApplicationCreatedNotification
My controller function:
public function store(StoreApplicationRequest $request, Task $task)
{
Gate::authorize('store', [Application::class, $task]);
$application = null;
DB::transaction(function () use ($request, $task, &$application) {
$application = $task->applications()->create([
...$request->validated(),
'user_id' => auth()->id(),
]);
$application->load('user');
$application->setRelation('task', $task);
});
ApplicationCreated::dispatch($application);
return response()->json($application, 201);
}
My handle function in the Listener:
public function handle(ApplicationCreated $event): void
{
$recipient = $event->application->task->contact_email;
$name = $event->application->task->contact_firstname . ' ' . $event->application->task->contact_lastname;
Notification::route('mail', [
$recipient => $name,
])
->notify(new ApplicationCreatedNotification($event->application));
// @TODO: Send notification to the applicant as well
// something like this:
// "You successfully applied to the task: $task->title"
}
And My Test:
public function test_user_can_apply_to_task(): void
{
$user = User::factory()->organization()->create();
$orga = Organization::factory()->forUser($user)->create();
$task = Task::factory()->forOrganization($orga)->create();
$basicUser = User::factory()->create();
Event::fake();
Notification::fake();
$response = $this->actingAs($basicUser)->post(route('applications.store', $task->id));
Event::assertDispatched(ApplicationCreated::class);
Notification::assertSentOnDemand(ApplicationCreatedNotification::class, function (ApplicationCreatedNotification $notification, array $channels, object $notifiable) use ($task) {
$recipient = $task->contact_email;
$name = $task->contact_firstname . ' ' . $task->contact_lastname;
return $notifiable->routes['mail'] === [$recipient => $name];
});
$response->assertStatus(201);
$this->assertDatabaseHas('applications', [
'task_id' => $task->id,
'user_id' => $basicUser->id,
]);
}
I get this output:
FAILED Tests\Feature\CreateApplicationTest > user can apply to task
The expected [App\Notifications\ApplicationCreatedNotification] notification was not sent.
Failed asserting that false is true.
at vendor\laravel\framework\src\Illuminate\Support\Testing\Fakes\NotificationFake.php:89
85▕ if (is_numeric($callback)) {
86▕ return $this->assertSentToTimes($notifiable, $notification, $callback);
87▕ }
88▕
➜ 89▕ PHPUnit::assertTrue(
90▕ $this->sent($notifiable, $notification, $callback)->count() > 0,
91▕ "The expected [{$notification}] notification was not sent."
92▕ );
93▕ }
1 vendor\laravel\framework\src\Illuminate\Support\Testing\Fakes\NotificationFake.php:89
2 vendor\laravel\framework\src\Illuminate\Support\Testing\Fakes\NotificationFake.php:54
I know that the handle function gets called because when I add dd($recipient, $name); exit(0);
the test process stops and I get this output
"[email protected]" // app\Listeners\SendApplicationCreatedNotification.php:33
"Katrin Thomas" // app\Listeners\SendApplicationCreatedNotification.php:33
After some debugging, I found out that a NotificationSent event gets triggered when executing my test. This event gets Dispatched by the NotificationSender
class:
protected function sendToNotifiable($notifiable, $id, $notification, $channel)
{
if (! $notification->id) {
$notification->id = $id;
}
if (! $this->shouldSendNotification($notifiable, $notification, $channel)) {
return;
}
$response = $this->manager->driver($channel)->send($notifiable, $notification);
$this->events->dispatch(
new NotificationSent($notifiable, $notification, $channel, $response)
);
}
All fake Notifications get "send" by the NotificationFake class which implements the NotificationDispatcher which is an alias of the Dispatch interface.
It looks like when sending the notification onDemand it does not use the NotificationFake class as its dispatcher.
Does anyone has an Idea why this doesn't work?
Upvotes: 0
Views: 134
Reputation: 1511
By reading the docs I found out that calling Event::fake()
will prevent the event listener from running. Therefore the notifications will not be sent. So removing Event::fake()
solves my problem.
Upvotes: 0