الاختبارات و TDD

اختبار البريد الإلكتروني والإشعارات

25 دقيقة الدرس 21 من 35

اختبار البريد الإلكتروني والإشعارات

يعد اختبار البريد الإلكتروني والإشعارات أمرًا بالغ الأهمية لضمان تواصل تطبيقك بفعالية مع المستخدمين. في هذا الدرس، سنستكشف استراتيجيات شاملة لاختبار البريد والإشعارات وقوائم الانتظار والبث دون إرسال رسائل بريد إلكتروني فعلية أو تشغيل إشعارات حقيقية أثناء الاختبارات.

لماذا نختبر الاتصالات؟

يضمن اختبار أنظمة البريد الإلكتروني والإشعارات:

  • استلام المستلمين الصحيحين للرسائل
  • دقة محتوى الرسالة وتنسيقها بشكل صحيح
  • عمل المرفقات والصور المضمنة بشكل صحيح
  • إرسال مهام قوائم الانتظار بشكل صحيح
  • عمل قنوات الإشعارات (البريد، الرسائل النصية، Slack، إلخ) كما هو متوقع
  • وصول أحداث البث إلى القنوات الصحيحة
مهم: لا ترسل أبدًا رسائل بريد إلكتروني أو إشعارات حقيقية أثناء الاختبارات الآلية. استخدم دائمًا البدائل المزيفة أو المحاكاة لمنع البريد المزعج وحماية بيانات المستخدمين.

أساسيات اختبار البريد في Laravel

يوفر Laravel واجهة Mail مع طريقة fake() قوية تعترض جميع البريد الصادر أثناء الاختبارات:

<?php namespace Tests\Feature; use App\Mail\WelcomeEmail; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Mail; use Tests\TestCase; class UserRegistrationTest extends TestCase { use RefreshDatabase; public function test_welcome_email_is_sent_on_registration() { // منع الإرسال الفعلي للبريد الإلكتروني Mail::fake(); // إجراء التسجيل $response = $this->post('/register', [ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'password123', 'password_confirmation' => 'password123', ]); // التأكد من إرسال البريد الإلكتروني Mail::assertSent(WelcomeEmail::class, function ($mail) { return $mail->hasTo('john@example.com') && $mail->hasSubject('Welcome to Our Platform'); }); } }

تأكيدات البريد المتقدمة

يوفر Laravel العديد من طرق التأكيد لاختبار رسائل البريد الإلكتروني:

// التأكد من إرسال بريد إلكتروني محدد Mail::assertSent(OrderShipped::class); // التأكد من إرسال البريد إلى مستخدم محدد Mail::assertSent(OrderShipped::class, function ($mail) use ($user) { return $mail->hasTo($user->email); }); // التأكد من إرسال البريد N مرة Mail::assertSent(InvoiceGenerated::class, 3); // التأكد من عدم إرسال البريد Mail::assertNotSent(CancelledOrder::class); // التأكد من عدم إرسال أي رسائل بريد Mail::assertNothingSent(); // التأكد من وضع البريد في قائمة الانتظار (لم يتم إرساله فورًا) Mail::assertQueued(NewsletterEmail::class); // التأكد من أن البريد يحتوي على بيانات محددة Mail::assertSent(OrderShipped::class, function ($mail) { return $mail->order->id === 123 && $mail->hasAttachment('/path/to/invoice.pdf'); });

اختبار محتوى البريد الإلكتروني

يمكنك عرض رسائل البريد الإلكتروني مباشرة في الاختبارات للتحقق من محتواها:

<?php namespace Tests\Unit; use App\Mail\OrderConfirmation; use App\Models\Order; use Tests\TestCase; class OrderConfirmationMailTest extends TestCase { public function test_email_contains_order_details() { $order = Order::factory()->create([ 'total' => 99.99, 'order_number' => 'ORD-12345', ]); $mailable = new OrderConfirmation($order); // عرض HTML للبريد الإلكتروني $content = $mailable->render(); // التأكد من المحتوى $this->assertStringContainsString('ORD-12345', $content); $this->assertStringContainsString('$99.99', $content); $this->assertStringContainsString('Order Confirmation', $content); } public function test_email_has_correct_subject_and_from() { $order = Order::factory()->create(); $mailable = new OrderConfirmation($order); $mailable->assertSeeInHtml('Order Confirmation'); $mailable->assertFrom('orders@example.com'); $mailable->assertHasSubject('Your Order #' . $order->order_number); } }

اختبار الإشعارات

يمكن اختبار نظام الإشعارات في Laravel بطريقة مماثلة باستخدام واجهة Notification:

<?php namespace Tests\Feature; use App\Models\User; use App\Notifications\AccountVerified; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Notification; use Tests\TestCase; class EmailVerificationTest extends TestCase { use RefreshDatabase; public function test_notification_sent_on_email_verification() { Notification::fake(); $user = User::factory()->create([ 'email_verified_at' => null, ]); // التحقق من البريد الإلكتروني $this->actingAs($user) ->post('/email/verify'); // التأكد من إرسال الإشعار Notification::assertSentTo( $user, AccountVerified::class ); } public function test_notification_sent_via_correct_channels() { Notification::fake(); $user = User::factory()->create(); $user->notify(new AccountVerified()); Notification::assertSentTo($user, AccountVerified::class, function ($notification, $channels) { return in_array('mail', $channels) && in_array('database', $channels); } ); } }

اختبار قنوات الإشعارات المتعددة

عند اختبار الإشعارات التي تستخدم قنوات متعددة (البريد، الرسائل النصية، Slack، إلخ)، تحقق من كل قناة بشكل مستقل:

<?php public function test_urgent_notification_sent_via_all_channels() { Notification::fake(); $admin = User::factory()->create(['role' => 'admin']); // تشغيل إشعار عاجل $this->post('/incidents/create', [ 'severity' => 'critical', 'message' => 'Server down', ]); Notification::assertSentTo($admin, UrgentIncident::class, function ($notification, $channels) { // التحقق من القنوات الثلاث return count($channels) === 3 && in_array('mail', $channels) && in_array('sms', $channels) && in_array('slack', $channels); } ); } public function test_notification_contains_correct_data() { Notification::fake(); $user = User::factory()->create(); $order = Order::factory()->create(); $user->notify(new OrderShipped($order)); Notification::assertSentTo($user, OrderShipped::class, function ($notification) use ($order) { $mailData = $notification->toMail($user); return $mailData->subject === 'Order Shipped' && $notification->order->id === $order->id; } ); }

اختبار مهام قوائم الانتظار

يتم إرسال العديد من رسائل البريد الإلكتروني والإشعارات عبر قوائم الانتظار. اختبر إرسال قائمة الانتظار بشكل منفصل:

<?php namespace Tests\Feature; use App\Jobs\SendBulkEmails; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Queue; use Tests\TestCase; class BulkEmailTest extends TestCase { use RefreshDatabase; public function test_bulk_email_job_is_dispatched() { Queue::fake(); $users = User::factory()->count(100)->create(); // تشغيل البريد الجماعي $this->post('/admin/send-newsletter', [ 'subject' => 'Monthly Update', 'body' => 'Newsletter content...', ]); // التأكد من دفع المهمة إلى قائمة الانتظار Queue::assertPushed(SendBulkEmails::class, function ($job) { return $job->recipientCount === 100; }); } public function test_job_pushed_to_correct_queue() { Queue::fake(); $this->post('/admin/send-newsletter', [/* ... */]); Queue::assertPushedOn('emails', SendBulkEmails::class); } public function test_job_not_pushed_when_validation_fails() { Queue::fake(); $this->post('/admin/send-newsletter', [ 'subject' => '', // غير صالح ]); Queue::assertNotPushed(SendBulkEmails::class); } }

اختبار الإشعارات في قوائم الانتظار

ادمج اختبار الإشعارات وقوائم الانتظار للإشعارات المؤجلة:

<?php public function test_notification_is_queued() { Notification::fake(); Queue::fake(); $user = User::factory()->create(); // إرسال الإشعار (يجب أن يتم وضعه في قائمة الانتظار) $user->notify((new InvoiceGenerated())->onQueue('notifications')); // التأكد من وضع الإشعار في قائمة الانتظار، وليس إرساله فورًا Notification::assertSentTo($user, InvoiceGenerated::class); // يمكن أيضًا التحقق من خصائص قائمة الانتظار Queue::assertPushed(function ($job) { return $job->queue === 'notifications'; }); }

اختبار أحداث البث

بالنسبة للميزات في الوقت الفعلي باستخدام البث (WebSockets، Pusher، إلخ)، استخدم واجهة Event:

<?php namespace Tests\Feature; use App\Events\MessageSent; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Event; use Tests\TestCase; class ChatTest extends TestCase { use RefreshDatabase; public function test_message_broadcast_on_send() { Event::fake([MessageSent::class]); $user = User::factory()->create(); $recipient = User::factory()->create(); $this->actingAs($user) ->post('/messages', [ 'recipient_id' => $recipient->id, 'body' => 'Hello!', ]); // التأكد من بث الحدث Event::assertDispatched(MessageSent::class, function ($event) use ($recipient) { return $event->message->recipient_id === $recipient->id && $event->message->body === 'Hello!'; }); } public function test_broadcast_to_correct_channel() { $user = User::factory()->create(); $message = Message::factory()->create(); $event = new MessageSent($message); // التأكد من قناة البث $this->assertEquals( 'user.'.$user->id, $event->broadcastOn()->name ); } }

اختبار مرفقات البريد الإلكتروني

تحقق من أن رسائل البريد الإلكتروني تتضمن المرفقات الصحيحة:

<?php public function test_invoice_email_has_pdf_attachment() { Mail::fake(); $order = Order::factory()->create(); // إرسال بريد الفاتورة Mail::to('customer@example.com')->send(new InvoiceMail($order)); Mail::assertSent(InvoiceMail::class, function ($mail) { $attachments = $mail->attachments; return count($attachments) === 1 && $attachments[0]['options']['mime'] === 'application/pdf' && str_contains($attachments[0]['file'], 'invoice.pdf'); }); } public function test_report_email_has_multiple_attachments() { Mail::fake(); $this->post('/reports/send', [ 'type' => 'monthly', 'attachments' => ['sales.csv', 'inventory.xlsx'], ]); Mail::assertSent(ReportMail::class, function ($mail) { return count($mail->attachments) === 2; }); }

اختبار تخزين الإشعارات في قاعدة البيانات

عند استخدام قناة إشعارات database، اختبر تخزين الإشعارات بشكل صحيح:

<?php public function test_notification_stored_in_database() { $user = User::factory()->create(); $user->notify(new NewFollower($follower)); // التأكد من وجود الإشعار في قاعدة البيانات $this->assertDatabaseHas('notifications', [ 'notifiable_id' => $user->id, 'notifiable_type' => User::class, 'type' => NewFollower::class, ]); // التحقق من بيانات الإشعار $notification = $user->notifications->first(); $this->assertEquals($follower->id, $notification->data['follower_id']); $this->assertNull($notification->read_at); } public function test_marking_notification_as_read() { $user = User::factory()->create(); $user->notify(new NewFollower($follower)); $notification = $user->unreadNotifications->first(); $notification->markAsRead(); $this->assertDatabaseHas('notifications', [ 'id' => $notification->id, ]); $this->assertNotNull($notification->fresh()->read_at); }
أفضل ممارسة: اختبر منطق الإشعارات بشكل منفصل عن آليات التسليم. اختبر أن الإشعار الصحيح تم إنشاؤه مع البيانات الصحيحة، ثم اختبر تسليمه عبر القنوات المتوقعة.

اختبار مع عمال قوائم الانتظار الحقيقيين

في بعض الأحيان تحتاج إلى اختبار التنفيذ الفعلي للمهمة، وليس الإرسال فقط:

<?php public function test_email_sent_after_job_processes() { // لا تزيف قائمة الانتظار، لكن زيف البريد Mail::fake(); $user = User::factory()->create(); // إرسال المهمة بشكل متزامن dispatch_sync(new SendWelcomeEmail($user)); // التأكد من إرسال البريد فعليًا Mail::assertSent(WelcomeEmail::class, function ($mail) use ($user) { return $mail->hasTo($user->email); }); }

اختبار سيناريوهات فشل الإشعارات

اختبر ما يحدث عندما تفشل الإشعارات:

<?php public function test_handles_invalid_email_address() { Mail::fake(); $user = User::factory()->create([ 'email' => 'invalid-email', // غير صالح ]); try { $user->notify(new AccountVerified()); $this->fail('Expected validation exception'); } catch (\Exception $e) { // السلوك المتوقع $this->assertStringContainsString('invalid', $e->getMessage()); } Mail::assertNotSent(AccountVerified::class); } public function test_notification_retries_on_failure() { Queue::fake(); $user = User::factory()->create(); // إنشاء مهمة يجب إعادة محاولتها dispatch(new SendCriticalAlert($user))->onQueue('high'); Queue::assertPushed(SendCriticalAlert::class, function ($job) { // التحقق من تكوين إعادة المحاولة return $job->tries === 3 && $job->backoff === 60; }); }
تحذير: كن حذرًا مع عدد التأكيدات. إذا كان الكود الخاص بك يرسل رسائل بريد إلكتروني أو إشعارات متعددة، فإن ()assertSent بدون معامل عد سينجح طالما تم إرسال واحدة على الأقل.

اختبار ترجمة البريد الإلكتروني

اختبر أن رسائل البريد الإلكتروني يتم إرسالها باللغة الصحيحة:

<?php public function test_email_sent_in_user_preferred_language() { Mail::fake(); $user = User::factory()->create([ 'locale' => 'es', // الإسبانية ]); $user->notify(new OrderShipped($order)); Mail::assertSent(OrderShipped::class, function ($mail) use ($user) { $mailData = $mail->toMail($user); // التحقق من أن الموضوع بالإسبانية return $mailData->subject === 'Pedido Enviado' && str_contains($mail->render(), 'Su pedido ha sido enviado'); }); }

اختبار قنوات الحضور للبث

بالنسبة لقنوات الحضور، اختبر التفويض:

<?php namespace Tests\Feature; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class PresenceChannelTest extends TestCase { use RefreshDatabase; public function test_user_can_join_chat_room_presence_channel() { $user = User::factory()->create(); $response = $this->actingAs($user) ->post('/broadcasting/auth', [ 'channel_name' => 'presence-chat-room.1', ]); $response->assertOk(); $response->assertJson([ 'channel_data' => [ 'user_id' => $user->id, 'user_info' => [ 'name' => $user->name, ], ], ]); } public function test_unauthorized_user_cannot_join_private_room() { $user = User::factory()->create(); $privateRoom = Room::factory()->private()->create(); $response = $this->actingAs($user) ->post('/broadcasting/auth', [ 'channel_name' => "presence-chat-room.{$privateRoom->id}", ]); $response->assertForbidden(); } }
تمرين 1: أنشئ مجموعة اختبارات لبريد إعادة تعيين كلمة المرور يتحقق من: (1) إرسال البريد إلى المستخدم الصحيح، (2) تضمين رمز إعادة التعيين في البريد، (3) صلاحية رابط إعادة التعيين، (4) انتهاء صلاحية البريد بعد 60 دقيقة، و(5) ترجمة البريد بناءً على تفضيل المستخدم.
تمرين 2: اكتب اختبارات لإشعار متعدد القنوات (بريد إلكتروني + رسائل نصية + إشعار دفع) يتحقق من استلام كل قناة لتنسيق الرسالة الصحيح وأن القنوات الاحتياطية تُستخدم عند فشل القنوات الأساسية.
تمرين 3: اختبر نظام حملة بريد إلكتروني مؤجل يرسل رسائل بريد إلكتروني مختلفة بناءً على شرائح المستخدمين، مع ضمان: (1) تجميع المهام بشكل صحيح، (2) احترام تحديد المعدل، (3) إعادة محاولة الرسائل الفاشلة، و(4) تتبع مقاييس الحملة.

الملخص

في هذا الدرس، غطينا استراتيجيات اختبار شاملة لأنظمة البريد الإلكتروني والإشعارات:

  • استخدام بدائل Mail وNotification المزيفة لمنع الإرسال الفعلي أثناء الاختبارات
  • اختبار محتوى البريد والمرفقات والمستلمين
  • التحقق من قنوات الإشعارات وطرق التسليم
  • اختبار رسائل البريد الإلكتروني والإشعارات المؤجلة مع بدائل قوائم الانتظار المزيفة
  • اختبار أحداث البث وقنوات الحضور
  • اختبار الإشعارات المخزنة في قاعدة البيانات
  • معالجة الإشعارات الفاشلة ومنطق إعادة المحاولة
  • اختبار ترجمة البريد الإلكتروني والتخصيص

تضمن تقنيات الاختبار هذه عمل أنظمة الاتصالات في تطبيقك بشكل موثوق دون إرسال رسائل اختبار إلى مستخدمين حقيقيين أو إغراق الخدمات الخارجية أثناء الاختبار الآلي.