lundi 26 août 2019

Removal of SerializesModels on Queued Event Listeners does not take effect when mocking on unit tests

I'm currently working with Laravel's Queued Event Listeners on an Eloquent model's saving event. On my event class, I have set up a public property that contains my model which I can then access inside the $event object within my listener via $event->myModel.

The handle() method on my listener compares old values from the new one so I removed Illuminate\Queue\SerializesModels on my event class. This works fine and I'm now able to compare old values from the new one.

The issue is with testing. I am making a test that checks if the old and new value that I'm getting inside my listener's handle() method is different. However, the $event->myModel->getOriginal() now returns an empty array. This only happens when testing.

Is this a bug?

I tried setting a public $originalModelProps on my event class however, it returns empty when the handle() method on my listener is called.

I'm not using Illuminate\Queue\SerializesModels or any trait/class/interface named SerializesModels on my code.

Events/Models/MyModel/MyModelSavedEvent.php

<?php

namespace App\Events\Models\MyModel;

use App\Models\MyModel;
use Illuminate\Queue\SerializesModels;

class MyModelSavedEvent
{
    /**
     *
     * @var \App\Models\MyModel
     */
    public $myModel;

    public $originalAccessLevelProps;

    /**
     * Create a new event instance.
     * 
     * @param \App\Models\MyModel
     *
     * @return void
     */
    public function __construct(MyModel $myModel)
    {
        $this->myModel = $myModel;
        $this->originalAccessLevelProps = $this->myModel->getOriginal('level');
    }
}


Listeners/Models/MyModel/MyModelSavedEventListener.php

<?php

namespace App\Listeners\Models\MyModel;

use Log;
use App\Models\MyModel;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Events\Models\MyModel\MyModelSavedEvent;

class MyModelSavedEventListener implements ShouldQueue
{
    /**
     * The queue connection that should handle the job.
     *
     * @var string
     */
    public $connection = 'sqs';

    /**
     * The number of times the job may be attempted.
     *
     * @var int
     */
    public $tries = 3;

    /**
     * The number of seconds the job can run before timing out.
     *
     * @var int
     */
    public $timeout = 60;

    public function __construct()
    {
        // ..
    }

    /**
     * This job should only be queued if the level of myModel
     * has been updated.
     *
     * @param  \App\Events\Models\MyModel\MyModelSavedEvent $event
     * 
     * @return bool
     */
    public function shouldQueue(MyModelSavedEvent $event)
    {
        $prevLevel = $event->myModel->getOriginal('level');
        $currLevel = $event->myModel->level;

        return $prevLevel !== $currLevel;
    }

    public function handle(MyModelSavedEvent $event): void
    {
        $prevLevel = $event->myModel->getOriginal('level');
        $currLevel = $event->myModel->level;

        Log::info([
            'Running handle!',
            $event->myModel->getOriginal(), // This returns an empty array
            $event->originalAccessLevelProps, // This is null
            $event->myModel->id, // This returns a string
            $prevLevel, // This returns null
            $currLevel // This contains the latest value
        ]);

        // ..
    }
}


tests/app/Listeners/Models/MyModel/MyModelSavedEventListener.php

<?php

public function testSample()
{
    Queue::fake();

    $myModel = MyModel::where(...)->first();

    Queue::assertNothingPushed();

    // This will trigger the event
    $myModel->level = 'new level';
    $myModel->save();

    Queue::assertPushedOn('sqs', MyModelSavedEventListener::class);
}

I was expecting that similar to how it works outside of the test, I'll be able to compare old and new values.



via Chebli Mohamed

Aucun commentaire:

Enregistrer un commentaire