Inject Event Dispatcher for Testability in Laravel Services

📖 2 minutes read

When dispatching events in Laravel services, inject the Dispatcher instead of using the event() helper function. This makes your code more testable and explicit about its dependencies.

The Problem with event()

Most Laravel code uses the event() helper to dispatch events:

class OrderProcessor
{
    public function process($order)
    {
        // ... processing logic ...
        
        event(new OrderCompleted($order));
    }
}

This works fine, but it makes testing harder. You can’t easily mock the event system, and it’s not immediately clear that this class has a dependency on Laravel’s event system.

The Solution: Dependency Injection

Instead, inject the Illuminate\Events\Dispatcher:

use Illuminate\Events\Dispatcher;

class OrderProcessor
{
    public function __construct(
        private Dispatcher $dispatcher
    ) {}
    
    public function process($order)
    {
        // ... processing logic ...
        
        $this->dispatcher->dispatch(
            new OrderCompleted($order)
        );
    }
}

Why This Matters

Testing becomes trivial:

public function test_processing_dispatches_event()
{
    $dispatcher = Mockery::mock(Dispatcher::class);
    $dispatcher->shouldReceive('dispatch')
        ->once()
        ->with(Mockery::type(OrderCompleted::class));
    
    $processor = new OrderProcessor($dispatcher);
    $processor->process($order);
}

Dependencies are explicit: Anyone reading the constructor knows immediately that this class dispatches events. No hidden global dependencies.

Framework agnostic: You could swap the event system by implementing the same interface. The event() helper ties you to Laravel’s globals.

When to Use Which

Use event() in:

  • Controllers (already framework-coupled)
  • Blade views (convenience matters more)
  • Quick scripts and seeders

Use injected Dispatcher in:

  • Services that need testing
  • Domain logic classes
  • Anywhere you want explicit dependencies

The pattern extends to other Laravel facades too: inject Illuminate\Contracts\Cache\Repository instead of using Cache::get(), inject Illuminate\Contracts\Queue\Queue instead of Queue::push(), and so on.

Your tests will thank you.

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *