Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Laravel-way of testing how many times an event has fired?

I am using the base PHPUnit config file with the default TestCase.php file.

I am seeking a way to check if an event is fired multiple times during a test. Current code looks like:

function test_fires_multiple_events()
{
    $this->expectsEvents(SomeEvent::class);
}

Ideally, I would want:

function test_fires_multiple_events()
{
    $this->expectsEvents(SomeEvent::class)->times(3);
}
like image 583
Chris Avatar asked Jan 07 '23 09:01

Chris


1 Answers

After walking through the code in MocksApplicationServices, I was able to piggyback the trait, and the fields it captures when the withoutEvents() method.

The extension would look like:

function test_fires_multiple_events()
{
    $this->withoutEvents(); // From MocksApplicationServices

    $c = collect($this->firedEvents)
                ->groupBy(function($item, $key) { return get_class($item); })
                ->map(function($item, $key) { return $item->count(); });

    $this->assertsEqual(3, $c->get(SomeEvent::class));

}

To step through what collection is doing:

1) collect($this->firedEvents): Takes the captured events and stores them in a collection. It's important to note, that MocksApplicationServices pushes each event into an array, meaning it is already keeping count.

2) groupBy(function($item, $key) { return get_class($item); }): Groups by the class name, meaning we have a collection of arrays now.

3) map(function($item, $key) { .. }: Simply tally up the children.

This results in a structure like:

Illuminate\Support\Collection (1) (
    protected items -> array (2) [
        'App\Events\SomeEvent' => integer 2
        'App\Events\SomeOtherEvent' => integer 1
    ]
)

Edit Just to clean it up a bit - you could add the following to your base test case file:

public function assertEventFiredTimes($event, $count)
{
    $this->assertEquals(
        count(collect($this->firedEvents)
            ->groupBy(function($item, $key) { return get_class($item); })
            ->get($event, [])),
        $count
    );
}

And then in your tests:

$this->assertEventFiredTimes(SomeEvent::class, 3);

Not forgetting to add withoutEvents().

like image 195
Chris Avatar answered Jan 23 '23 04:01

Chris