Framework: Testing issue while mocking Events

Created on 7 Jul 2017  路  6Comments  路  Source: laravel/framework

  • Laravel Version: 5.4.16 - 5.4.28+Latest
  • PHP Version: 7.1

Description:

In Laravel 5.4.15, Mocking Event is working fine using

Event::fake();
updateSetting($companyId, $info);
Event::assertDispatched(SettingEvent::class, function($e) {
 return something;
});

Afte I update from 5.4.15 to 5.4.28, mocking Event::fake() not working.

Event::fake();
Model::setEventDispatcher(Event::getFacadeRoot());
updateSetting($companyId, $info);
Event::assertDispatched(SettingEvent::class, function($e) {
 return $e->message === 'Your settings have been updated';
});

In my Setting model, we have broadcasted event.

static::updated(function ($model) {
            broadcast(new SettingsEvent($model->company_id, 'Your settings have been updated'))->toOthers();
        });
class SettingEvent implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    private $companyId;

    // The elements to be broadcasted has to be public.
    public $message;


    public function __construct($companyId, $message)
    {
        $this->companyId = $companyId;
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('company.'.$this->companyId);
    }
}

when running phpunit tests,

The expected [App\Events\SettingEvent] event was not dispatched.
Failed asserting that false is true.

Could you please let us know why its not working for us.

Most helpful comment

In case someone comes across this and can't get the Model::setEventDispatcher(Event::getFacadeRoot()); line working and needs a quick hacky solution, save the result of getFacadeRoot first, then call the fake method and then reapply the facade root. Otherwise your just applying the fake dispatcher again.

$initialDispatcher = Event::getFacadeRoot();
Event::fake();
Model::setEventDispatcher($initialDispatcher);

Again, this is a hacky solution. A slightly more elegant one would be extending the event facade and adding a parameter where you can turn faking the model events on or off. I'm sure there is a "proper way" to handle the use case of only faking custom events as opposed custom events and model events, but it's eluded me thus far.

All 6 comments

Event name CompanySettingsEvent doesn't match the one you're asserting with SettingEvent

@themsaid It is SettingEvent Its my typo mistake :disappointed:

class SettingEvent implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    private $companyId;

    // The elements to be broadcasted has to be public.
    public $message;


    public function __construct($companyId, $message)
    {
        $this->companyId = $companyId;
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('company.'.$this->companyId);
    }
}

I want to reopen this issue.

@themsaid My team really needs resolution of this issue. Our app's event mocking tests all fail after upgrading from Laravel 5.4.15 to 5.4.16 or any later version. This sure looks like a breaking change in a trivial version release.

This change is a result of a fix that was submitted in 5.4.16, previously while faking events, model events would still get fired which was wrong, after the fix model events won't get fired since the dispatcher is faked.

In your code you call an event after an event is fired, and since the dispatcher is mocked the first event won't get fired, and as a result your second event won't get fired.

In case someone comes across this and can't get the Model::setEventDispatcher(Event::getFacadeRoot()); line working and needs a quick hacky solution, save the result of getFacadeRoot first, then call the fake method and then reapply the facade root. Otherwise your just applying the fake dispatcher again.

$initialDispatcher = Event::getFacadeRoot();
Event::fake();
Model::setEventDispatcher($initialDispatcher);

Again, this is a hacky solution. A slightly more elegant one would be extending the event facade and adding a parameter where you can turn faking the model events on or off. I'm sure there is a "proper way" to handle the use case of only faking custom events as opposed custom events and model events, but it's eluded me thus far.

Was this page helpful?
0 / 5 - 0 ratings