Level 61
You can change to dependency injection and easily provide mock or test double.
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();
}
}
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
Please or to participate in this conversation.