Pengujian Notifikasi
Notifikasi panel memiliki dua sisi yang menjawab dua pertanyaan berbeda — "apakah pengguna diberi tahu" dan "apakah pengguna masih dapat menemukannya nanti" — dan sebuah notifikasi memang dapat hanya memenuhi salah satunya. Lima helper mencakup kedua sisi tersebut dan sengaja dipisahkan: broadcast yang tidak dipersist akan hilang saat tab ditutup, sehingga assertion "terkirim" dan "tersimpan" tidak dapat saling menggantikan.
Contoh minimal yang berfungsi
<?php
declare(strict_types=1);
use App\Models\User;
use PandaPanel\Notifications\Notification;
it('tells the user their export is ready', function (): void {
$user = User::factory()->create();
fakePanelNotifications();
Notification::make('export-ready')->title('Export ready')->send($user);
assertPanelNotificationSentTo($user, 'Export ready');
});2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Helper tidak memerlukan import karena dimuat otomatis melalui files milik Composer.
Apa yang sebenarnya diuji
Notification::send() melakukan dua hal, dalam urutan berikut:
if ($this->persistent && method_exists($user, 'notify')) {
$user->notify(new PanelDatabaseNotification($this->toArray()));
}
if ($this->broadcast) {
event(new PanelNotificationSent($user, $this->toArray()));
}2
3
4
5
6
7
Assertion broadcast melihat event. Assertion penyimpanan membaca $user->notifications(). persistent() off secara default, sedangkan broadcast() on, sehingga Notification::make(...)->send($user) biasa hanya menghasilkan toast dan tidak menyimpan apa pun.
Semua helper
| Function | Signature | Membaca |
|---|---|---|
fakePanelNotifications | fakePanelNotifications(): void | memasang Event::fake([PanelNotificationSent::class]) |
assertPanelNotificationSentTo | assertPanelNotificationSentTo(Authenticatable $user, ?string $title = null): void | event yang didispatch |
assertNoPanelNotifications | assertNoPanelNotifications(): void | event yang didispatch |
assertPanelNotificationStoredFor | assertPanelNotificationStoredFor(Authenticatable $user, ?string $title = null): void | database |
assertNoPanelNotificationsStoredFor | assertNoPanelNotificationsStoredFor(Authenticatable $user): void | database |
Setiap helper mendelegasikan pekerjaan ke PandaPanel\Testing\TestsNotifications, yang bersifat public dan sepenuhnya static:
use PandaPanel\Testing\TestsNotifications;
TestsNotifications::fake();
TestsNotifications::assertSentTo($user, 'Export ready');
TestsNotifications::assertNothingSent();
TestsNotifications::assertStoredFor($user, 'Export ready');
TestsNotifications::assertNothingStoredFor($user);2
3
4
5
6
7
fakePanelNotifications()
Event::fake([PanelNotificationSent::class]);Scope fake sengaja dibuat sempit. Melakukan fake terhadap semua event akan mematikan model event yang dibutuhkan panel, dan test bisa tetap lulus ketika lifecycle hook resource sebenarnya rusak. Panggil helper ini sebelum code yang sedang diuji — event yang dikirim sebelum fake dipasang tidak akan tercatat.
assertPanelNotificationSentTo()
Assertion mencocokkan $event->user->getAuthIdentifier() dan, jika title diberikan, juga $event->payload['title']:
it('broadcasts to the right user', function (): void {
$user = User::factory()->create();
fakePanelNotifications();
Notification::make('saved')->title('Saved.')->send($user);
assertPanelNotificationSentTo($user); // any title
assertPanelNotificationSentTo($user, 'Saved.'); // this title
});2
3
4
5
6
7
8
9
10
Title yang dibandingkan adalah title yang sudah di-resolve. Jika notifikasi tidak mendeklarasikan title, Str::headline($name) digunakan. Karena itu Notification::make('export-ready')->send($user) dapat dicocokkan dengan 'Export Ready'.
assertNoPanelNotifications()
it('says nothing when the action was refused', function (): void {
fakePanelNotifications();
// … code that should notify nobody …
assertNoPanelNotifications();
});2
3
4
5
6
7
Di bawahnya menggunakan Event::assertNotDispatched(), jadi assertion ini berlaku untuk keseluruhan test, bukan hanya satu pengguna.
assertPanelNotificationStoredFor()
Assertion ini membaca row database, bukan event:
it('leaves a notification the user can find later', function (): void {
$user = User::factory()->create();
Notification::make('export')->title('Export ready')->persistent()->send($user);
assertPanelNotificationStoredFor($user, 'Export ready');
});2
3
4
5
6
7
Tidak diperlukan fake, dan fake juga tidak membantu. Assertion melakukan query melalui $user->notifications() dan membaca data['title'] dari setiap row. Jika title tidak diberikan, assertion hanya memastikan pengguna memiliki setidaknya satu notifikasi tersimpan.
assertNoPanelNotificationsStoredFor()
it('does not fill the bell with "Saved."', function (): void {
$user = User::factory()->create();
Notification::make('saved')->title('Saved.')->send($user);
assertNoPanelNotificationsStoredFor($user);
});2
3
4
5
6
7
Assertion ini menangkap notifikasi yang tanpa sengaja diberi persistent(), salah satu penyebab notification center cepat menjadi penuh dan sulit digunakan.
Menguji payload tanpa mengirim
Notification::toArray() bersifat pure, sehingga bentuk payload dapat diuji secara langsung tanpa user, fake, maupun database:
use PandaPanel\Notifications\Notification;
$payload = Notification::make('import-failed')->warning()->body('Row 12')->toArray();
expect($payload['title'])->toBe('Import Failed')
->and($payload['type'])->toBe('warning')
->and($payload['icon'])->toBe('triangle-alert')
->and($payload['body'])->toBe('Row 12');2
3
4
5
6
7
8
| Builder method | Signature |
|---|---|
make | static make(?string $name = null): self |
title | title(string $title): self |
body | body(string $body): self |
icon | icon(string $icon): self |
color | color(NotificationColor $color): self |
success / info / warning / danger | (): self |
actions | actions(array $actions): self |
persistent | persistent(bool $persistent = true): self |
broadcast | broadcast(bool $broadcast = true): self |
send | send(Authenticatable $user): self |
toArray | toArray(): array |
Kedua flag layak diuji sendiri ketika sebuah notifikasi memang sengaja hanya menggunakan satu channel:
$notification = Notification::make('report')->persistent()->broadcast(false);
expect($notification->isPersistent())->toBeTrue()
->and($notification->isBroadcast())->toBeFalse();2
3
4
Notifikasi yang dikirim action panel
Alasan paling umum menggunakan helper ini adalah action, import, atau export yang mengirim notifikasi sebagai efek samping. Pasang fake, jalankan proses melalui mekanisme yang sesuai, lalu lakukan assertion:
use App\Panels\Admin\Resources\Users\UserResource;
it('notifies the administrator after a bulk verification', function (): void {
fakePanelNotifications();
$this->post('/admin/actions/bulk', [
'resource' => 'users',
'action' => 'verify',
'records' => [$unverified->id],
]);
assertPanelNotificationSentTo($this->admin);
});2
3
4
5
6
7
8
9
10
11
12
13
Untuk queued import atau export, method pada job tetaplah method biasa. Memanggil failed() secara langsung adalah cara menguji failure path tanpa harus membuat job benar-benar gagal saat dieksekusi. Lihat Queued import.
Hal yang perlu diperhatikan
- Fake sebelum eksekusi, assert setelahnya.
Event::fakehanya merekam event yang didispatch setelah fake dipasang. - Fake tidak menghentikan persistence.
Event::fakemencegat broadcast, bukan pemanggilannotify(), sehingga assertion penyimpanan tetap bekerja meskipun fake aktif. Kedua sisi memang sengaja independen sehingga satu test dapat menguji keduanya sekaligus. - Title dibandingkan secara persis.
'Export ready.'tidak sama dengan'Export ready'. Abaikan argumen title jika copy bukan bagian yang sedang diuji. assertNoPanelNotificationsStoredFor()lulus untuk model tanpa methodnotifications(). Tidak ada yang dapat ditanyakan sehingga hasilnya dianggap kosong. Jika penggunaan traitNotifiableadalah bagian yang ingin diuji, assert secara terpisah.fakePanelNotifications()hanya mencakup satu class event.PandaPanel\Broadcasting\PanelNotification— event toast yang lebih sederhana — tidak ikut di-fake. GunakanEvent::fake([PanelNotification::class])untuk event tersebut.- Testbench tidak memiliki broadcaster. Assertion terhadap shared prop
broadcastingperluConfig::set('broadcasting.default', …)terlebih dahulu. Jika tidak, nilainyaenabled: falsedan test menguji kondisi yang salah.
Lihat juga
- Helper pengujian dan setup pengujian
- Notifikasi: pengujian — endpoint, shared prop, channel callback, dan flash bridge
- Toast notification, database notification
- Notification center, broadcasting
- Queued import dan queued export