Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 5.2 Event Testing: expectsEvent not seeing the event fired although it is being fired

I have been trying to test events and I had it working yesterday. That was before I started to refactor the test code to keep it from being too repetitious. I added the setUp method call to generate the fake data using ModelFactories. This was done in each test case yesterday and as stated it was working.

I am thinking it has something to do with using the setUp method but I have no idea why that would be the case. First I attempted to use setUpBeforeClass() static method as that is only run once on unit test runs. However the laravel application is not actually setup until the first call to setUp()... A possible bug maybe? It is documented in this SO post Setting up PHPUnit tests in Laravel.

Therefore I opted to use the setUp method and just check to see if the static property is null or not, if it is null then it generates the data, if not it just goes on it's way.

Here is the output from running phpunit on the project

➜  project git:(laravel-5.2-testing) ✗ phpunit
PHPUnit 5.2.10 by Sebastian Bergmann and contributors.

E                                                                  1 / 1 (100%)

Time: 8.94 seconds, Memory: 33.50Mb

There was 1 error:

1) UserEmailNotificationsTest::testNotificationSentOnGroupMediaSaving
Exception: These expected events were not fired: [\App\Events\GroupMediaSaving]

/Users/jcrawford/Dropbox/Work/Viddler/Repositories/l5_media_communities/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MocksApplicationServices.php:44
/Users/jcrawford/Dropbox/Work/Viddler/Repositories/l5_media_communities/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:127

FAILURES!
Tests: 1, Assertions: 0, Errors: 1, Skipped: 8.

Here is my code for the unit test file that I have created.

<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class UserEmailNotificationsTest extends \TestCase
{
    use DatabaseTransactions;

    const COMMUNITIES_TO_CREATE = 3;
    const USERS_TO_CREATE = 10;
    const ADMINS_TO_CREATE = 5;

    protected static $communities = null;
    protected static $users = null;
    protected static $admins = null;

    public function setUp()
    {
        parent::setUp(); // TODO: Change the autogenerated stub

        if(is_null(self::$communities)) {
            self::$communities = factory(\Community::class, self::COMMUNITIES_TO_CREATE)->create()->each(function ($community) {
                self::$users[$community->id] = factory(User::class, self::USERS_TO_CREATE)->create()->each(function (\User $user) use ($community) {
                    $user->community()->associate($community);
                    $user->save();
                });

                self::$admins[$community->id] = factory(User::class, self::ADMINS_TO_CREATE, 'superadmin')->create()->each(function (\User $admin) use ($community) {
                    $admin->community()->associate($community);
                    $admin->save();
                });

                $community->save();
            });
        }
    }

    public static function getRandomCommunityWithAssociatedData()
    {
        $community = self::$communities[mt_rand(0, count(self::$communities)-1)];
        return ['community' => $community, 'users' => self::$users[$community->id], 'admins' => self::$admins[$community->id]];
    }

    /**
     * Test that the notification event is fired when a group media
     * item is saved.
     */
    public function testNotificationSentOnGroupMediaSaving()
    {
        $data = self::getRandomCommunityWithAssociatedData();

        // FOR SOME REASON THIS SAYS THE EVENT IS NEVER FIRED WHEN IT ACTUALLY IS FIRED.
        $this->expectsEvents(['\App\Events\GroupMediaSaving']);

        $community = $data['community'];
        $admin = $data['admins'][0];
        $user = $data['users'][0];

        $asset = factory(Asset\Video::class)->make();
        $asset->community()->associate($community);
        $asset->user()->associate($admin);
        $asset->save();

        $group = factory(Group::class)->make();
        $group->community()->associate($community);
        $group->created_by = $admin->id;
        $group->save();


        $groupMedia = factory(GroupMedia::class)->make();
        $groupMedia->asset()->associate($asset);
        $groupMedia->user()->associate($user);
        $groupMedia->group()->associate($group);
        $groupMedia->published_date = date('Y-m-d H:i:s', strtotime('-1 day'));
        $groupMedia->save();

        // I can print_r($groupMedia) here and it does have an ID attribute so it was saved, I also put some debugging in the event object and it is actually fired.....
    }
}

Any thoughts as to why it doesn't see the events being fired? I find it odd that they are fired if I create the models inside the test case but seem to be failing when done inside of setUp(). The worst part is I am not creating the GroupMedia model in the setUp method rather that is done in the test case.

I have also dumped the data that is returned from the getRandomCommunityWithAssociatedData method and it is returning proper model objects all with id attributes which tells me they were all saved to the database during creation.

As requested here is the code that is actually firing the event, it is located in the GroupMedia model in the static boot method.

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

    static::saving(function($groupMedia) {
        Event::fire(new \App\Events\GroupMediaSaving($groupMedia));
    });
}
like image 230
Joseph Crawford Avatar asked Mar 18 '16 17:03

Joseph Crawford


1 Answers

If you look at the source code for expectsEvents (inside the trait Illuminate/Foundation/Testing/Concerns/MocksApplicationServices), you will see that it calls the function withoutEvents, which mocks the application event dispatcher, suppressing and collecting all future events.

The problem for you is that the setUp function will have already been called at this point, so your events will not be caught and logged by the test, and will not show up when the assertion is evaluated.

In order to correctly see the events firing, you should make sure that you declare the assertion before the code which triggers the events.

like image 168
beeglebug Avatar answered Sep 20 '22 23:09

beeglebug