Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

EndeavourPlatforms's avatar

Testing Events in Artisan commands

We'd like to test running some artisan commands, using

$this->artisan('command argument');

However, we don't want to fully black-box test this. We can mock classes using app()->instance() to bind mocks, that works fine.

What we can't seem to get working, is $this->withoutEvents() and other event testing features.

Is there some way we can test, fake or 'catch' events when running artisan commands for testing?

0 likes
3 replies
bobbybouwmann's avatar

Yeah, you can use the facade for that

public function testMyAwesomeFeature()
{
    Event::fake();

    $this->artisan(MyAwesomeCommand::class)->assertExitCode(0);

    Event::assertDispatched(MyAwesomeEvent::class);
}

This is the basic gist. You can find more info here: https://laravel.com/docs/6.x/mocking#event-fake

EndeavourPlatforms's avatar

That's what I would expect to work, but it did not, for me:

My test looks like this:

function it_outputs_a_built_product_group_record()
{
    Event::fake();

    // ...

    $command = $this->artisan('debug:productgroup SomeValue testing');
    $command->assertExitCode(0);

    // ...

    $this->assertEventFired(DumpEvent::class, function () { return true; });
}

The always true callback is just to test the basics of this.

However, this fails with "The expected [App\Events\Log\DumpEvent] event was not dispatched.".

What is curious, is that the Event::fake() does do something, since the event is no longer handled normally, so it does capture events. It does not, however, detect it has having been fired.

The name (FQN) of the event is definitely correct.

In Laravel's EventFake, the dispatch() method is called as normal. The event is seen passing in, and $this->shouldFakeEvent($name, $payload) is true for the event, so nothing strange there; the event is stored correctly in the EventFake::$events property. It is, however, not there at the end of the test.

Figured it out: the problem is that this:

$command = $this->artisan('debug:productgroup SomeValue testing');
$command->assertExitCode(0);

does NOT have the same effect as this:

$this->artisan('debug:productgroup SomeValue testing')->assertExitCode(0);

With the first approach, the pending command is NOT fired until the test method ends; with the last approach, the command runs immediately. Probably a destructor trick.

Pretty obscure and easy to miss!

1 like
amnuts's avatar

I know this was written 5 years ago, but I have just been beating my head against a wall for a long time today trying to figure out why my event dispatch assertion was always failing, even though I could step debug it and see it was working! Eventually came across this post, and was the exact resolution I needed. Thanks, @endeavourplatforms!

Please or to participate in this conversation.