Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event Subscribers failed the tests even the events were fired #20294

Closed
arcanedev-maroc opened this issue Jul 28, 2017 · 4 comments
Closed

Event Subscribers failed the tests even the events were fired #20294

arcanedev-maroc opened this issue Jul 28, 2017 · 4 comments

Comments

@arcanedev-maroc
Copy link
Contributor

arcanedev-maroc commented Jul 28, 2017

  • Laravel Version: 5.4.30
  • PHP Version: 7.0
  • Database Driver & Version: SQLite (Testing)

Description:

I want to use the Event Subscribers to manage events & listeners in one place but the tests failed when i call the expectsEvents.

Steps To Reproduce:

User Model:

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The event map for the model.
     *
     * @var array
     */
    protected $events = [
        'creating' => \App\Events\Users\UserCreating::class,
        'created'  => \App\Events\Users\UserCreated::class,
        'saving'   => \App\Events\Users\UserSaving::class,
        'saved'    => \App\Events\Users\UserSaved::class,
        'deleting' => \App\Events\Users\UserDeleting::class,
        'deleted'  => \App\Events\Users\UserDeleted::class,
    ];
}

Events:

All the user events extend from the base event UserEvent:

\App\Events\Users\UserCreating
\App\Events\Users\UserCreated
\App\Events\Users\UserSaving
\App\Events\Users\UserSaved
\App\Events\Users\UserDeleting
\App\Events\Users\UserDeleted
<?php namespace App\Events\Users;

use App\User;

abstract class UserEvent
{
    public $user;

    public function __construct(User $user)
    {
        $this->user = $user;
    }
}

Event Subscriber:

<?php namespace App\Listeners;

use App\Events\Users\UserCreated;
use App\Events\Users\UserCreating;
use App\Events\Users\UserDeleted;
use App\Events\Users\UserDeleting;
use App\Events\Users\UserSaved;
use App\Events\Users\UserSaving;

class UserEventSubscriber
{
    public function onUserCreating($event)
    {
        var_dump('Creating');
    }

    public function onUserCreated($event)
    {
        var_dump('Created');
    }

    public function onUserSaving($event)
    {
        var_dump('Saving');
    }

    public function onUserSaved($event)
    {
        var_dump('Saved');
    }

    public function onUserDeleting($event)
    {
        var_dump('Deleting');
    }

    public function onUserDeleted($event)
    {
        var_dump('Deleted');
    }

    /**
     * Register the listeners for the subscriber.
     *
     * @param  \Illuminate\Events\Dispatcher  $dispatcher
     */
    public function subscribe($dispatcher)
    {
        $class  = self::class;
        $events = [
            UserCreating::class => "{$class}@onUserCreating",
            UserCreated::class  => "{$class}@onUserCreated",
            UserSaving::class   => "{$class}@onUserSaving",
            UserSaved::class    => "{$class}@onUserSaved",
            UserDeleting::class => "{$class}@onUserDeleting",
            UserDeleted::class  => "{$class}@onUserDeleted",
        ];

        foreach ($events as $event => $listener) {
            $dispatcher->listen($event, $listener);
        }
    }
}

Of course, i've registered the event subscriber in the EventServiceProvider.

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        'App\Events\SomeEvent' => [
            'App\Listeners\EventListener',
        ],
    ];

    protected $subscribe = [
        \App\Listeners\UserEventSubscriber::class,
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();

        //
    }
}

Test

<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;
    use DatabaseTransactions;

    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $this->expectsEvents([
            \App\Events\Users\UserSaving::class,
            \App\Events\Users\UserSaved::class,
            \App\Events\Users\UserCreating::class,
            \App\Events\Users\UserCreated::class,
        ]);

        $users = factory(\App\User::class, 1)->create();
    }
}

When i ran phpunit, this is what i got:

PHPUnit 5.7.21 by Sebastian Bergmann and contributors.

..F                                                                 3 / 3 (100%)
string(6) "Saving"
string(8) "Creating"
string(7) "Created"
string(5) "Saved"


Time: 1.31 seconds, Memory: 20.00MB

There was 1 failure:

1) Tests\Unit\ExampleTest::it_test_events
These expected events were not fired: [App\Events\Users\UserSaving, App\Events\Users\UserSaved, App\Events\Users\UserCreating, App\Events\Users\UserCreated]
Failed asserting that an array is empty.

FAILURES!
Tests: 3, Assertions: 2, Failures: 1.
@arcanedev-maroc
Copy link
Contributor Author

arcanedev-maroc commented Jul 28, 2017

@arcanedev-maroc
Copy link
Contributor Author

arcanedev-maroc commented Jul 28, 2017

I've found a workaround by overriding the withoutEvents():

/**
 * Mock the event dispatcher so all events are silenced and collected.
 *
 * @return $this
 */
protected function withoutEvents()
{
    $mock = Mockery::mock(EventsDispatcherContract::class);

    $mock->shouldReceive('fire', 'dispatch')->andReturnUsing(function ($called) {
        $this->firedEvents[] = $called;
    });

    $this->app->instance('events', $mock);

    return $this;
}

TO:

/**
 * Mock the event dispatcher so all events are silenced and collected.
 *
 * @return $this
 */
protected function withoutEvents()
{
    $mock = Mockery::mock(EventsDispatcherContract::class);

    foreach (['fire', 'until'] as $method) {
        $mock->shouldReceive($method, 'dispatch')->andReturnUsing(function ($called) {
            $this->firedEvents[] = $called;
        });
    }

    $this->app->instance('events', $mock);
    \Illuminate\Database\Eloquent\Model::setEventDispatcher($mock);

    return $this;
}

Now all my tests are green !!!

@themsaid
Copy link
Member

themsaid commented Aug 2, 2017

#20296 (comment)

@themsaid themsaid closed this as completed Aug 2, 2017
@Stetzon
Copy link

Stetzon commented Aug 16, 2017

I am having a similar problem with the events not being faked using a User Observer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants