How To Mock a Nested Laravel Artisan Command

I recently refactored a piece of Laravel code into its own artisan command so I could call it from the CLI and during runtime. This piece of code involved calling two nested artisan commands using the convenient call method. What I was not prepared for was how long it was going to take to test it. In my case, the nested commands already had tests, so repeating those assertions would have been redundant and wasteful, and mocking the nested commands was not as straightforward as I had hoped. My first thought was to mock the Artisan facade, but that mocked all artisan commands, including the parent command. Making it a partial only led to new errors. Eventually, after digging through the code and some trial and error, I discovered that I could mock the run method on the command classes and override their original bindings. Let's go through an example that demonstrates what I mean, but first, let's make sure that we have everything we need: You'll need to install composer Create a new Laravel project. Install Mockery: composer require --dev mockery/mockery Now that we have a working Laravel project, let's create the new commands by running the following: php artisan make:command NestedCommandA php artisan make:command NestedCommandB php artisan make:command ParentCommand In the app/Console/Commands folder, you should see that three new commands have been added. Update the commands to log some information to the console so we can include them in our tests later on. NestedCommandA

Apr 21, 2025 - 07:48
 0
How To Mock a Nested Laravel Artisan Command

I recently refactored a piece of Laravel code into its own artisan command so I could call it from the CLI and during runtime. This piece of code involved calling two nested artisan commands using the convenient call method. What I was not prepared for was how long it was going to take to test it. In my case, the nested commands already had tests, so repeating those assertions would have been redundant and wasteful, and mocking the nested commands was not as straightforward as I had hoped.

My first thought was to mock the Artisan facade, but that mocked all artisan commands, including the parent command. Making it a partial only led to new errors. Eventually, after digging through the code and some trial and error, I discovered that I could mock the run method on the command classes and override their original bindings.

Let's go through an example that demonstrates what I mean, but first, let's make sure that we have everything we need:

  1. You'll need to install composer
  2. Create a new Laravel project.
  3. Install Mockery: composer require --dev mockery/mockery

Now that we have a working Laravel project, let's create the new commands by running the following:

php artisan make:command NestedCommandA
php artisan make:command NestedCommandB
php artisan make:command ParentCommand

In the app/Console/Commands folder, you should see that three new commands have been added. Update the commands to log some information to the console so we can include them in our tests later on.

NestedCommandA



namespace App\Console\Commands;

use Illuminate\Console\Command;

class NestedCommandA extends Command
{
    protected $signature = 'app:nested-command-a';

    public function handle()
    {
        $this->info('Executing Nested Command A');
    }
}

NestedCommandB



namespace App\Console\Commands;

use Illuminate\Console\Command;

class NestedCommandB extends Command
{
    protected $signature = 'app:nested-command-b';

    public function handle()
    {
        $this->info('Executing Nested Command B');
    }
}

ParentCommand



namespace App\Console\Commands;

use Illuminate\Console\Command;

class ParentCommand extends Command
{
    protected $signature = 'app:parent-command';

    public function handle()
    {
        $this->info('Executing Parent Command');
        $this->call('app:nested-command-a');
        $this->call('app:nested-command-b');
    }
}

In our tests, we will create a partial mock of the nested commands and register them to the original classes. This follows the approach in the Laravel documentation except for some slight modifications:



namespace Tests\Unit;

use App\Console\Commands\NestedCommandA;
use App\Console\Commands\NestedCommandB;
use Mockery;
use Mockery\MockInterface;
use Tests\TestCase;

class ParentCommandTest extends TestCase
{
    public function test_parent_command_calls_nested_commands()
    {
        $this->instance(
            NestedCommandA::class,
            Mockery::mock(new NestedCommandA, function (MockInterface $mock) {
                $mock->shouldReceive('run')->once()->andReturn(0);
            })->makePartial()
        );

        $this->instance(
            NestedCommandB::class,
            Mockery::mock(new NestedCommandB, function (MockInterface $mock) {
                $mock->shouldReceive('run')->once()->andReturn(0);
            })->makePartial()
        );

        $this->artisan('app:parent-command')
            ->expectsOutput('Executing Parent Command')
            ->doesntExpectOutput('Executing Nested Command A')
            ->doesntExpectOutput('Executing Nested Command B')
            ->assertExitCode(0);
    }
}

In the CLI, when you run php artisan test, you should see a passing test, indicating that all expectations were met.

In our tests, we have created a mock of our commands, set an expectation on the run method and overridden the respective class bindings. Behind the scenes, Laravel manages the bindings by storing a key-value pair of class names and instances. When the test is run, the values for the command classes are replaced with mock instances. This ensures that when the Laravel service container is resolving the class names, our mocks are returned and the run method call is recorded.

I hope you find this helpful. Feel free to leave some questions or comments down below.