تزييف الأحداث لأغراض الاختبار Mocking

من موسوعة حسوب

مقدمة

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

يزوّد Laravel بمجموعة من المساعدات لتقليد أصل الأحداث والأعمال ‎(Jobs)‎ والواجهات الساكنة ‎(Facades‎)‎. تزود هذه المساعدات بشكل أساسي طبقة مساعدة مبنية على Mockery حتى لا تحتاج إلى القيام باستدعاءات معقدة لتوابع Mockery. بالطبع، يمكنك استخدام Mockery أو PHPUnit لإنشاء تقليداتك الخاصة.

تزييف Bus

كبديل عن تقليد الأصل، يمكنك استخدام التابع fake الخاص بالواجهة الساكنة Bus كبديل عن تقليد الأصل وذلك لمنع إرسال الأعمال. عند استخدام المزيفات، تُقام الإثباتات بعد تنفيذ مصدر الاختبار:

<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Jobs\ShipOrder;
use Illuminate\Support\Facades\Bus;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
   public function testOrderShipping()
   {
       Bus::fake();

       // القيام بشحن الطلبات..

       Bus::assertDispatched(ShipOrder::class, function ($job) use ($order) {
           return $job->order->id === $order->id;
       });

       // إثبات أن العمل لم يتم إرساله..
       Bus::assertNotDispatched(AnotherJob::class);
   }
}

تزييف الأحداث

كبديل عن تقليد الأصل، يمكنك استخدام التابع fake الخاص بالواجهة الساكنة Event لمنع جميع منصتي الأحداث من التنفيذ. يمكنك بعد ذلك التأكد أن الأحداث تم تفعيلها وتعقب البيانات التي تلقّتها. عند استخدام المزيفات، تُقام الإثباتات بعد تنفيذ مصدر الاختبار:

<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Events\OrderShipped;
use App\Events\OrderFailedToShip;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
   /**
    * اختبار شحن الطلبات.
    */
   public function testOrderShipping()
   {
       Event::fake();

       // القيام بشحن الطلبات..

       Event::assertDispatched(OrderShipped::class, function ($e) use ($order) {
           return $e->order->id === $order->id;
       });

       // التأكد من أن الحدث تم تفعيله مرّتين..
       Event::assertDispatched(OrderShipped::class, 2);

       // التأكد من أن الحدث لم يتم تفعيله..
       Event::assertNotDispatched(OrderFailedToShip::class);
   }
}

ملاحظة: بعد استدعاء Event::fake، لن ينفذ أي منصت أحداث. لذلك، إذا كان اختبارك يستخدم معامل النماذج التي تعتمد على الأحداث، مثل إنشاء UUID عند الحدث creating لنموذج، يجب أن تستدعي Event::fake بعد استخدامك للمعامل.

التزييفات النطاقية للأحداث

إذا أردت أن تزيف منصتي الأحداث من أجل مجموعة من اختباراتك، يمكنك استخدام التابع fakeFor:

<?php

namespace Tests\Feature;

use App\Order;
use Tests\TestCase;
use App\Events\OrderCreated;
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
   /**
    * اختبار عملية الطلب
    */
   public function testOrderProcess()
   {
       $order = Event::fakeFor(function () {
           $order = factory(Order::class)->create();

           Event::assertDispatched(OrderCreated::class);

           return $order;
       });

       // سيتم تفعيل الأحداث بشكل عادي وتشغيل المراقبات ..
       $order->update([...]);
   }
}

تزييف البريد

يمكنك استخدام التابع fake الخاص بالواجهة الساكنة Mail لمنع البريد من الإرسال. يمكنك بعد ذلك التأكد من أن ما يرسل عبر البريد (mailables) قد تم إرساله للمستخدمين وتعقب البيانات التي تلقّوها. عند استخدام المزيفات، يتم القيام بالإثباتات بعد تنفيذ مصدر الاختبار:

<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
   public function testOrderShipping()
   {
       Mail::fake();

       // القيام بشحن الطلبات ..

       Mail::assertSent(OrderShipped::class, function ($mail) use ($order) {
           return $mail->order->id === $order->id;
       });

       // التأكد من أن الرسالة قد أُرسلت للمستخدم ..
       Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
           return $mail->hasTo($user->email) &&
                  $mail->hasCc('...') &&
                  $mail->hasBcc('...');
       });

       // التأكد من أن الرسالة قد أُرسلت مرّتين ..
       Mail::assertSent(OrderShipped::class, 2);

       // التأكد من أن الرسالة لم ترسل ..
       Mail::assertNotSent(AnotherMailable::class);
   }
}

إذا كنت تضع ما يمكن إرساله عبر البريد في طابور معين من أجل الإرسال في الخلفية، استخدم التابع assertQueued بدلًا من التابع assertSent:

Mail::assertQueued(...);
Mail::assertNotQueued(...);

تزييف الإشعارات

يمكنك استخدام التابع fake الخاص بالواجهة الساكنة Notification لمنع الإشعارات من أن تُرسل. يمكنك بعد ذلك التأكد من أن الإشعارات قد أُرسلت، وتعقب البيانات التي تلقّتها. عند استخدام المزيفات، تُقام الإثباتات بعد تنفيذ مصدر الاختبار:

<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Notifications\OrderShipped;
use Illuminate\Support\Facades\Notification;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
   public function testOrderShipping()
   {
       Notification::fake();

       // القيام بشحن الطلبات ..

       Notification::assertSentTo(
           $user,
           OrderShipped::class,
           function ($notification, $channels) use ($order) {
               return $notification->order->id === $order->id;
           }
       );

       // التأكد من أن الإشعار أُرسل للمستخدمين المحددين ..
       Notification::assertSentTo(
           [$user], OrderShipped::class
       );

       // التأكد من أن الإشعار لم يُرسل ..
       Notification::assertNotSentTo(
           [$user], AnotherNotification::class
       );
   }
}

تزييف الطابور

كبديل عن تقليد الأصل، يمكنك استخدام التابع fake الخاص بالواجهة الساكنة Queue لمنع الأعمال من أن تُدرج في الطابور. يمكنك بعد ذلك التأكد من أن الأعمال قد تم إدراجها في الطابور وتعقب البيانات التي تلقّتها. عند استخدام المزيفات، تُقام الإثباتات بعد تنفيذ مصدر الاختبار:

<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Jobs\ShipOrder;
use Illuminate\Support\Facades\Queue;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
   public function testOrderShipping()
   {
       Queue::fake();

       // القيام بشحن الطلبات ..

       Queue::assertPushed(ShipOrder::class, function ($job) use ($order) {
           return $job->order->id === $order->id;
       });

       // التأكد من أن العمل قد تم إدراجه في الطابور ..
       Queue::assertPushedOn('queue-name', ShipOrder::class);

       // التأكد من أن العمل قد تم إدراجه مرّتين ..
       Queue::assertPushed(ShipOrder::class, 2);

       // التأكد من أن العمل لم يتم إدراجه ..
       Queue::assertNotPushed(AnotherJob::class);
   }
}

تزييف التخزين

يسمح التابع fake الخاص بالواجهة الساكنة Storage بتوليد قرص تخزين وهمي، الذي يسهل عملية اختبار رفع الملفات عند دمجه بمساعدات توليد الملفات الخاصة بالصنف UploadedFile. مثال:

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
   public function testAvatarUpload()
   {
       Storage::fake('avatars');

       $response = $this->json('POST', '/avatar', [
           'avatar' => UploadedFile::fake()->image('avatar.jpg')
       ]);

       // التأكد من أن الملف قد تم تخزينه ..
       Storage::disk('avatars')->assertExists('avatar.jpg');

       // التأكد من عدم وجود الملف ..
       Storage::disk('avatars')->assertMissing('missing.jpg');
   }
}

ملاحظة: سيحذف التابع fake افتراضيًّا كل الملفات في المجلد المؤقت. إذا أردت الاحتفاظ بهذه الملفات، يمكنك استخدام التابع persistentFake بدلًا من التابع fake.

الواجهات الساكنة

على عكس استدعاءات التوابع الساكنة العادية، يمكن تقليد أصل الواجهات الساكنة. سيؤدي هذا بفائدة كبيرة مقارنة بالتوابع الساكنة ويعطي نفس قابليّة الاختبار التي تحصل عليها عند إضافة الاعتماديات. عند الاختبار، قد تحتاج إلى تقليد أصل واجهة ساكنة معينة في إحدى وحدات التحكم الخاصة بك. على سبيل المثال، لدينا وحدة التحكم التالية:

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

class UserController extends Controller
{
   /**
    * إظهار قائمة بجميع مستخدمي التطبيق
    *
    * @return Response
    */
   public function index()
   {
       $value = Cache::get('key');

       //
   }
}

يمكننا تقليد أصل الاستدعاء للواجهة الساكنة Cache عن طريق التابع shouldReceive، الذي سيعيد كائن تقليد من Mockery. نظراً لكون الواجهات الساكنة معالجة ومدارة من حاوي الخدمة الخاص بإطار Laravel، فهي تحتوي على قابلية اختبار أكبر بكثير من الأصناف الساكنة العادية. على سبيل المثال، تقليد أصل استدعاء التابع get الخاص بالواجهة Cache:

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Support\Facades\Cache;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class UserControllerTest extends TestCase
{
   public function testGetIndex()
   {
       Cache::shouldReceive('get')
                   ->once()
                   ->with('key')
                   ->andReturn('value');

       $response = $this->get('/users');

       // ...
   }
}

ملاحظة: لا يجب عليك أن تقلد الواجهة الساكنة Request. بدلاً من ذلك، يمكنك تمرير المدخلات التي تريد باستخدام التوابع المساعدة في HTTP مثل get و post عند تشغيل اختباراتك. على غرار ذلك، عوضًا عن تقليد الواجهة الساكنة Config، استدع التابع Config::set في اختباراتك.

مصادر