PHP Unit Testing Laravel Events

Things to remember

  1. When you call event::fake(), no event listeners will be executed.
  2. You can assert only the events that faked not actually running events.
  3. Event faking method of Laravel can fake all events, selected events or all events except selected ones.
  4. You can check dispatched events are listened by correct event listener.

Event Faking options

Assert event is dispatched

 Event::assertDispatched(YourEventClass::class);

Assert same event dispatched multiple times

//Same Event is dispatched twice
Event::assertDispatched(YourEventClass::class, 2);

Assert event is listened by correct event listener

Event::assertListening(
    YourEventClass::class,
    EventListenerClass::class
);

Fake only selected event or events

Event::fake([
        YourEventClassOne::class,
        YourEventClassTwo::class,
    ]);

Fake all events

 Event::fake();

Fake all events except selected events

Event::fake()->except([
    YourEventClass::class,
]);

Check event is not dispatched

Event::assertNotDispatched(YourEventClass::class);

Check any event is not dispatched or fired.

Event::assertNothingDispatched();

Fake event only for a portion of your test. Image you want to fake event inside a code block without affecting other part of the code. Rest of the code also has event and need to run event listeners to pass the test correctly.

$result = Event::fakeFor(function () {
            //execute your code (request, factory, service, etc.) that will fire the event

            Event::assertDispatched(YourEventClass::class);
 
            //Return anything you want to continue the test. 
           //Remove return if nothing to return
            return true; 

        });

Preparing a sample project with events to test

Lets setup our Laravel project and create sample events and event listeners to test.

Step 1: Setup your Laravel Project.

Step 2: Create two event classes as ItemShipped and OrderPlaced.

php artisan make:event ItemShipped
php artisan make:event OrderPlaced

Step 3: Create two event listener classes for above events

For ItemShipped event

php artisan make:listener NotifyShippingManager

For OrderPlaced event

php artisan make:listener NotifyOwner

Step 4: Bind event listeners to listen on events

NotifyShippingManager listener should listen for ItemShipped event. (Just type hint the event in handle method.)

namespace App\Listeners;

use App\Events\ItemShipped;
use Illuminate\Support\Facades\Log;

class NotifyShippingManager
{
    /**
     * Handle the event.
     */
    public function handle(ItemShipped $event): void
    {
        Log::info("Handle method of event Listener running..");
    }

}

NotifyOwner listener should listen for OrderPlaced event. (Just type hint the event in handle method.)

namespace App\Listeners;
use App\Events\OrderPlaced;
use Illuminate\Support\Facades\Log;

class NotifyOwner
{
    /**
     * Handle the event.
     */
    public function handle(OrderPlaced $event): void
    {
        Log::info('Handle method of Notify Owner');
    }
}

Step 5: Dispatch ItemShipped event when application received a get request to root domain.

use App\Events\ItemShipped;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    Log::info('Dispatch event');
    ItemShipped::dispatch();
});

Testing Events using PHP Unit Testing

Below shows list of use cases for testing events. First create the test file using below artisan command. Follow naming convention like below example. Make sure to end the name of your test file name with “Test”.

php artisan make:test ItemShippedTest

Now use below artisan command to test above created test file using PHP unit testing.

php artisan test --filter=ItemShippedTest

Case 1: Test event is dispatched

Dispatch single event when application received the get request on root domain.

Route::get('/', function () {
    Log::info('Dispatch event');
    ItemShipped::dispatch();
});

Modify test file to check whether event is fired. Make sure to run this test using the artisan command given above.

namespace Tests\Feature;

use App\Events\ItemShipped;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;

class ItemShippedTest extends TestCase
{
    /**
     * A basic feature test example.
     */
    public function test_example(): void
    {
        Event::fake();
        $this->get('/');
        Event::assertDispatched(ItemShipped::class);
    }
}

Case 2: Test same event dispatched 2 times

Modify web.php file to dispatch same event twice.

Route::get('/', function () {
    Log::info('Dispatch events');
    ItemShipped::dispatch();
    ItemShipped::dispatch();
});

Testing whether same event fired twice.

namespace Tests\Feature;

use App\Events\ItemShipped;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;

class ItemShippedTest extends TestCase
{
    /**
     * A basic feature test example.
     */
    public function test_example(): void
    {
        Event::fake();
        $this->get('/');
        Event::assertDispatched(ItemShipped::class, 2);
    }
}

Case 3: Test event is not dispatched

You can use assertNotDispatched to check whether OrderPlaced event is not dispatched since it was not declared in the web.php file

namespace Tests\Feature;

use App\Events\ItemShipped;
use App\Events\OrderPlaced;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;

class ItemShippedTest extends TestCase
{
    /**
     * A basic feature test example.
     */
    public function test_example(): void
    {
        Event::fake();
        $this->get('/');
        Event::assertDispatched(ItemShipped::class);
        Event::assertNotDispatched(OrderPlaced::class);
    }
}

Case 4: Use two events and make sure to one event executed without faking and second event executed using event faking.

Execute both event classes from web.php

<?php

use App\Events\ItemShipped;
use App\Events\OrderPlaced;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    Log::info('Dispatch events');
    ItemShipped::dispatch();
    OrderPlaced::dispatch();
});

You cannot assert OrderPlaced event since that event was not faked. Only faked events can be asserted.

<?php

namespace Tests\Feature;

use App\Events\ItemShipped;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;

class ItemShippedTest extends TestCase
{
    /**
     * A basic feature test example.
     */
    public function test_example(): void
    {
        Event::fake(ItemShipped::class);
        $this->get('/');
        Event::assertDispatched(ItemShipped::class);
    }
}