wipflash's avatar

Mutation test failed with artisan command

I am failing with the mutation test due to Artisan::call('migration') command. How I fix this.

Mutation fails on:

1) /builds/api/app/Console/Commands/MigrateAllCommand.php:42    [M] MethodCallRemoval
--- Original
+++ New
@@ @@
                 $this->setRequestClient($systemName);
                 $this->loadSystemSettings();
                 $this->connectSystemDatabase();
-                \Artisan::call('migrate');
+
                 $this->info('Migration successful for system ' . $systemName);
                 $this->resetSystemDatabaseConnection();
             }

Command handle method:

public function handle()
    {
        $systemNames = \ApplicationSettingsAllClients::getAll()->pluck(self::SYSTEM_NAME_CODE);
        if (!empty($systemNames)) {
            foreach ($systemNames as $systemName) {
                $this->setRequestClient($systemName);
                $this->loadSystemSettings();
                $this->connectSystemDatabase();
                \Artisan::call('migrate');

                $this->info('Migration successful for system ' . $systemName);
                $this->resetSystemDatabaseConnection();
            }
        }
    }

Unit tests:

<?php

namespace Tests\unit\App\Console\Commands;

use Tests\TestCase;

class MigrateAllCommandTest extends TestCase
{
    /**
     * @var bool
     */
    public $mockConsoleOutput = false;

    /**
     * @covers \App\Console\Commands\MigrateAllCommand::handle
     */
    public function testHandleWhenEmpty()
    {
        $collection = \Mockery::mock(\Illuminate\Support\Collection::class);

        \ApplicationSettingsAllClients::shouldReceive('getAll')
            ->andReturn($collection);

        $collection->shouldReceive('pluck')
            ->withArgs(['043'])
            ->andReturnNull();

        $command = \Mockery::mock(\App\Console\Commands\MigrateAllCommand::class)
        ->makePartial();

        $command->handle();
    }

    /**
     * @covers \App\Console\Commands\MigrateAllCommand::handle
     * @covers \App\Console\Commands\MigrateAllCommand::setRequestClient
     */
    public function testHandle()
    {
        $collection = \Mockery::mock(\Illuminate\Support\Collection::class);

        \ApplicationSettingsAllClients::shouldReceive('getAll')
            ->andReturn($collection);

        $collection->shouldReceive('pluck')
            ->withArgs(['043'])
            ->andReturn(['unit-test-migration-1', 'unit-test-migration-2']);

        $command = \Mockery::mock(\App\Console\Commands\MigrateAllCommand::class)
            ->shouldAllowMockingProtectedMethods()
            ->makePartial();

        $command->shouldReceive('setRequestClient')
            ->withArgs(['unit-test-migration-1'])
            ->once()
            ->andReturnSelf();

        $command->shouldReceive('loadSystemSettings')
            ->twice()
            ->andReturn('unit-test-settings');

        $command->shouldReceive('connectSystemDatabase')
            ->twice()
            ->andReturnSelf();

        \Illuminate\Support\Facades\Artisan::shouldReceive('call')
            ->with('migrate')
            ->andReturnSelf();

        $command->shouldReceive('info')
            ->twice()
            ->andReturn('Migration successful for system unit-test-migration-1');

        $command->shouldReceive('resetSystemDatabaseConnection')
            ->twice()
            ->andReturnSelf();

        $command->handle();
    }
}
0 likes
3 replies
bugsysha's avatar

You can change to dependency injection and easily provide mock or test double.

wipflash's avatar

If you don't mind can I have an example?

bugsysha's avatar
bugsysha
Best Answer
Level 61

Command handle method:

public function handle(\App\Console\Kernel $kernel): void // this is dependency inejction
{
	$systemNames = \ApplicationSettingsAllClients::getAll()->pluck(self::SYSTEM_NAME_CODE);
        if (!empty($systemNames)) {
            foreach ($systemNames as $systemName) {
                $this->setRequestClient($systemName);
                $this->loadSystemSettings();
                $this->connectSystemDatabase();
                $kernel->call('migrate'); // here you replace facade with dependency injected object
                $this->info('Migration successful for system ' . $systemName);
                $this->resetSystemDatabaseConnection();
            }
        }
}

Then inside your tests you can mock or create a test double and specify expectations.

Also instead of injecting console kernel, you can type-hint the \Illuminate\Contracts\Console\Kernel interface.

That would change the signature of the handle method to:

public function handle(\Illuminate\Contracts\Console\Kernel $kernel): void
1 like

Please or to participate in this conversation.