@mattapril Hi, if you want to test against console events, you need to add the Illuminate\Foundation\Testing\WithConsoleEvents trait to your class test.
Laravel 11: asserting a command called another command
I have a command (MyCommand) that calls another third party (Passport) command by calling:
$this->call('passport:purge');
I want to test my command and verify that the 'passport:purge' command gets called as well. I've had this test working for years with this:
$purgeCommand = \Mockery::mock(PurgeCommand::class . '[handle]');
$purgeCommand->shouldReceive('handle')->once();
$this->app[Kernel::class]->registerCommand($purgeCommand);
$this->artisan(MyCommand::class);
However, as of Laravel 11/Passport 12 it no longer works due to the introduction of lazy commands using the AsCommand attribute. I get the following error:
ArgumentCountError: Too few arguments to function Symfony\Component\Console\Attribute\AsCommand::__construct(), 0 passed in /var/www/auth/vendor/mockery/mockery/library/Mockery/Loader/EvalLoader.php(30) : eval()'d code on line 31 and at least 1 expected
I've tried the following unsuccessfully:
- Different mock types: I was able to fix the above error, but I was unable to register my mock command.
- CommandStarting event: according to Laravel docs this event should be fired when a command starts, but this does not seem to be the case when I used
Event::fake() -
Artisan::fake(): I think this caused a conflict when trying to execute the primary command from my test. - Queue the command using
Artisan::queue(): this was the only thing that really worked, as I was able to check for the command usingQueue::fake(), but I don't really want to queue the command other than for the sake of being able to test it.
Are there any other approaches that I've overlooked?
@Amaury Thanks, although that only seemed to trigger events for the main command that I was calling, not the sub commands. I did however finally find a solution:
function testPassportPurgeCommandCalled() {
$artisanMock = \Mockery::mock(Artisan::getFacadeRoot())->makePartial();
$artisanMock->shouldReceive('call')->once()->with('passport:purge');
Artisan::swap($artisanMock);
$this->artisan(MyCommand::class);
}
And make sure you use Artisan::call('passport:purge') inside of MyCommand (not $this->call('passport:purge'))
Please or to participate in this conversation.