# Eventlar va Event Job larni Laravelda testlash

Ilovangiz uchun testlar yozish — bu kodingiz kutilganidek ishlashini ta’minlashning muhim qismi. Ammo, Laravel’da hodisalar va hodisa vazifalarini test qilish haqida gap ketganda, ishlar biroz qiyinlashishi mumkin. Ushbu blogda biz Laravel’da hodisalar va hodisa vazifalarini qanday test qilishni o‘rganamiz.

Birinchidan, Laravel’da hodisalar va hodisa vazifalari nima ekanligini tushunib olaylik.

Laravel’ning hodisalari oddiy obunachi-obzervatorni amalga oshirishni ta’minlaydi, bu bizga ilovangizda sodir bo‘ladigan turli hodisalarga obuna bo‘lish va ularni kuzatish imkonini beradi. Laravel standart bo‘yicha bir qancha hodisalarni taqdim etadi, masalan, model hodisalari va autentifikatsiya hodisalari, lekin siz ilovangizning ehtiyojlariga mos ravishda maxsus hodisalar ham yaratishingiz mumkin.

Hodisa vazifalari asosiy tarmoqni bloklamasdan fon rejimida biror vazifani bajarish usulidir. Ushbu vazifalar navbatga qo‘yilishi va asinxron tarzda qayta ishlanishi mumkin, bu sizning ilovangizga vazifa fon rejimida ishlayotgan paytda so‘rovlarni qayta ishlashni davom ettirish imkonini beradi. Laravel’da hodisa vazifalari odatda elektron pochta jo‘natish, hisobotlar yaratish yoki ma’lumotlarni qayta ishlash kabi bajarilishi uzoq vaqt talab qiladigan murakkab vazifalarni bajarish uchun ishlatiladi.

Endi hodisalar va hodisa vazifalarining nima ekanligini tushunganligimizdan so‘ng, ularni test qilishga kirishamiz.

### Hodisani test qilish

Faraz qilaylik, bizning mijozlar jadvalimizda mijozlar ro‘yxati mavjud va biz mijozlar bilan uchrashuv belgilashimiz va ma’lumotlarni uchrashuvlar jadvalimizga saqlashimiz kerak. Bizda uchrashuvlarga tegishli barcha narsalarni boshqarish uchun tizimimizda quyidagi model sinfi `Meeting` mavjud.

```php
class Meeting extends Model {
    protected $fillable = [
        'meeting_date',
        'meeting_time',
        'email_to',
        'mail_content',
        'status',
        'created_by',
    ];

    protected $dates = [
        'meeting_date',
    ];

    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by', 'id');
    }

    protected static function boot()
    {
        parent::boot();

        static::creating(function ($model) {
            $model->created_by = auth()->id();
        });
    }
}
```

Bizda uchrashuv ma’lumotlarini saqlash uchun `MeetingController` sinfi va quyidagi marshrut mavjud.

```php
use App\Controller;

class MeetingController extends Controller {

    public function store(MeetingRequest $request) {
        try { 
            $meeting  = Meeting::create($request->validated());

            MeetingCreated::dispatch($meeting);
        } catch(\Exception $exception){
            Log::error($exception->getMessage());

            return response()->json($meeting);
        }   

        return response()->json($meeting, 201);
    }
}
```

```php
Route::post('/meetings', [MeetingController::class, 'store'])->name('meetings.create');
```

Faraz qilaylik, mijoz bilan belgilangan uchrashuv haqida elektron pochta xabarini yuboradigan `MeetingCreated` hodisamiz mavjud.

```php
<?php

namespace App\Providers;

use App\Events\MeetingCreated;
use App\Listeners\SendMeetingEmail;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        MeetingCreated::class => [
            SendMeetingEmail::class,
        ],
    ];
}
```

Laravel’da hodisalarni test qilishda birinchi qadam hodisani ishga tushiradigan test yaratishdir. Masalan, [`meetings.store`](http://meetings.store) marshrut uchun kontroller amalini test qilishimiz mumkin, bu o‘z navbatida `MeetingCreated` hodisasini ishga tushiradi.

```php
namespace Tests\Feature;

use Tests\TestCase;
use App\Models\User;
use Illuminate\Support\Facades\Event;

class LeadMailControllerTest extends TestCase
{
    use RefreshDatabase;

    public function setUp(): void
    {
        parent::setUp();
        $this->actingAs(User::factory()->create());
    }

    /**
    * foydalanuvchi uchrashuv yaratishi mumkin
    *
    * @test
    */
    public function test_user_can_create_a_meeting()
    {
        Event::fake();

        $attributes = Meeting::factory()->make();
        $response   = $this->post(route('meetings.store', $attributes));

        $response->assertCreated();
        $this->assertDatabaseHas('meetings', $attributes);
        Event::assertDispatched(MeetingCreated::class);
    }
}
```

Ushbu testda biz `Event::fake()` usulidan foydalanib, hodisa jo‘natuvchisini soxtalashtiramiz, shuning uchun hodisa jo‘natilmaydi. Keyin [`meetings.store`](http://meetings.store) marshrutiga uchrashuv ma’lumotlarini jo‘natib, yangi uchrashuv yaratamiz, bu `MeetingCreated` hodisasini ishga tushiradi. Nihoyat, biz `Event::assertDispatched()` usulidan hodisaning jo‘natilganligini tasdiqlash uchun foydalanamiz.

Yuqoridagi test muvaffaqiyatli o‘tadi, ammo agar biz uchrashuvni yaratgan foydalanuvchi yaratuvchi sifatida belgilanganligini tekshirish uchun yuqoridagi kodga yangi tasdiqlash qo‘shsak, bu test muvaffaqiyatsiz bo‘ladi, chunki biz barcha hodisalarni soxtalashtirayapmiz.

```php
namespace Tests\Feature;

use Tests\TestCase;
use App\Models\User;
use Illuminate\Support\Facades\Event;

class LeadMailControllerTest extends TestCase
{
    use RefreshDatabase;

    public function setUp(): void
    {
        parent::setUp();
        $this->actingAs(User::factory()->create());
    }

    /**
    * foydalanuvchi uchrashuv yaratishi mumkin
    *
    * @test
    */
    public function test_user_can_create_a_meeting()
    {
        Event::fake();

        $attributes = Meeting::factory()->make();
        $response   = $this->post(route('meetings.store', $attributes));

        $response->assertCreated();
        $this->assertDatabaseHas('meetings', $attributes);
        Event::assertDispatched(MeetingCreated::class);

        // yaratuvchi belgilanganligini tekshirish
        $latestMeeting = Meeting::latest()->first();
        $this->assertEquals(auth()->id(), $latestMeeting->created_by);
    }
}
```

Ushbu muammoni hal qilish uchun faqat `MeetingCreated` hodisasini soxtalashtirishimiz kerak, quyida ko‘rsatilganidek.

```php
public function test_user_can_create_a_meeting()
{
    Event::fake([
        MeetingCreated::class,
    ]);

    $attributes = Meeting::factory()->make();
    $response   = $this->post(route('meetings.store', $attributes));

    $response->assertCreated();
    $this->assertDatabaseHas('meetings', $attributes);
    Event::assertDispatched(MeetingCreated::class);

    // yaratuvchi belgilanganligini tekshirish
    $latestMeeting = Meeting::latest()->first();
    $this->assertEquals(auth()->id(), $latestMeeting->created_by);
}
```

### Hodisa vazifasini va tinglovchini test qilish

Hodisa vazifalarini test qilishda avvalo, vazifani navbatga qo‘yuvchi testni yaratishimiz kerak. Masalan, keling, mijozga uchrashuv haqida elektron pochta xabarini jo‘natuvchi `SendMeetingEmail` hodisa vazifamiz bor deb faraz qilaylik. Mana shu hodisa vazifasi uchun misol test:

```php
/**
* @test
*/
public function test_send_meeting_email_job()
{
    Queue::fake();

    $meeting = Meeting::factory()->create();
    MeetingCreated::dispatch($meeting);

    Queue::assertPushed(CallQueuedListener::class, function ($job) {
        return $job->class == SendMeetingEmail::class;
    });
}
```

Ushbu testda biz `Queue::fake()` usulidan foydalanib, navbatni soxtalashtiramiz, shuning uchun vazifa bajarilmaydi. Keyin zavod yordamida yangi uchrashuv yaratamiz, bu `SendMeetingEmail` vazifasini navbatga qo‘yishi kerak. Nihoyat, biz `Queue::assertPushed()` usulidan vazifaning navbatga qo‘yilganligini tasdiqlash uchun foydalanamiz.

### Hodisa vazifasini bajarilishini test qilish

Biz vazifaning navbatga qo‘yilganligini tasdiqlaganimizdan so‘ng, vazifaning to‘g‘ribajarilganligini test qilishimiz mumkin. Mana `SendMeetingEmail` hodisa vazifasi va unga mos test uchun misol.

```php
namespace App\Listeners;

use App\Constants\Queue;
use App\Events\MeetingCreated;
use Illuminate\Contracts\Queue\ShouldQueue;

/**
 * Class SendMeetingEmail
 */
class SendMeetingEmail implements ShouldQueue
{
    /**
     * @var string $queue
     */
    public string $queue = Queue::EMAIL;

    /**
     * Hodisani boshqarish.
     *
     * @param MeetingCreated $event
     *
     * @return void
     */
    public function handle(MeetingCreated $event)
    {
        $meeting    = $event->meeting;

        Mail::to($meeting->email_to)
            ->queue((new MeetingEmail($meeting))
            ->onQueue(Queue::EMAIL));
    }
}
```

```php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

/**
 * Class MeetingEmail
 *
 * @package App\Notifications
 */
class MeetingEmail extends Mailable implements ShouldQueue
{
    use Queueable;
    use SerializesModels;

    /**
     * @var Meeting|null
     */
    public array $meeting;

    /**
     * Yangi xabar instance-ni yaratish.
     *
     * @return void
     */
    public function __construct(Meeting $meeting)
    {
        $this->meeting = $meeting;
    }

    /**
     * Xabarni yaratish.
     *
     * @return $this
     */
    public function build(): MeetingEmail
    {
        $mailable    = $this->from($this->meeting->creator->email, $this->meeting->creator->name);
        $mailable    = $mailable->subject("Uchrashuv tasdiqlandi");
        $mailable    = $mailable->text('email.meeting.index', ['meeting' => $this->meeting]);

        return $mailable;
    }
}
```

```php
public function test_send_meeting_email_job_execution()
{
    Mail::fake();

    $meeting = Meeting::factory()->create();
    $event    = new MeetingCreated($leadMail);
    event($event);

    Mail::assertQueued(MeetingEmail::class, 
        function ($mail) use ($meeting) {
            return $mail->hasTo($meeting->email_to) &&
                   $mail->hasFrom($meeting->creator->email) &&
                   $mail->hasSubject('Uchrashuv tasdiqlandi'); 
        }
    );
}
```

Ushbu testda biz `Mail::fake()` usulidan foydalanib, pochta jo‘natuvchini soxtalashtiramiz, shuning uchun elektron pochta jo‘natilmaydi. Keyin zavod yordamida yangi foydalanuvchini yaratamiz va `MeetingCreated` hodisasining yangi nusxasini yaratamiz va uni `event()` usuli yordamida jo‘natamiz. Keyin biz elektron pochta jo‘natilishi uchun navbatga qo‘yilganligini va elektron pochtaning turli xususiyatlarini, masalan, `hasTo`, `hasFrom`, `hasSubject` va boshqalarni tasdiqlashimiz mumkin.

### Xulosa

Laravel’ning hodisa tizimi bizning ilovamizda hodisalarga obuna bo‘lish va o‘zgarishlarni kuzatishning samarali usulini ta’minlaydi. Hodisa vazifalari ilovangizga murakkab vazifalarni fon rejimida bajarayotgan paytda so‘rovlarni qayta ishlashni davom ettirish imkonini beradi. Ammo, testni qachon va qanday boshlashni bilmaganda, odatda bu qiyinchilik tug‘diradi.

Hodisalarni test qilishda `Event::fake()` usulidan foydalanib, hodisa jo‘natuvchisini soxtalashtirish va hodisaning jo‘natilmasligini ta’minlash muhimdir. Ammo, ilovangizning boshqa qismlariga xalaqit bermaslik uchun faqat test qilish kerak bo‘lgan hodisalarni soxtalashtirish kerakligini unutmang.
