問題点
何らかの入力を求めるartisnaコマンドをphpunitでテストしようとした場合、ただartisanコマンドを実行するだけではいつまでも入力待ちの状態になりテストが行うことが出来ない。
例
Commandクラス
本当は入力値を使用してDBに書き込み・・・などすると思うが、ここでは必要最低限のコードのみを記載する。
TestCommand.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class TestCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'test_command';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$name = $this->ask('名前は?');
$this->info("あなたの名前は{$name}です。");
}
}
Kernelクラス
Kernel.php
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
\App\Console\Commands\TestCommand::class
];
通常のartisanコマンド実行
artisan
$ php artisan --ansi test_command
名前は?:
> 山田太郎
あなたの名前は山田太郎です。
上記artisanコマンドを実行するようなテストクラス
入力待ちのまま固まってしまいプロセスを強制終了するしかない。
CommandTest
class CommandTest extends TestCase
{
/**
* @see \MasterDataSeeder
*/
public function test001()
{
// $this->artisan('test_command');
}
}
回避方法
こちらを参考にさせてもらいました。
- http://themsaid.com/building-testing-interactive-console-20160409/
- https://github.com/laravel/framework/issues/11946
例
上記artisanコマンドを実行するようなテストクラス
ask()
をモック化して、常に山田太郎を返却するようにしています。
もしパスワードの入力などを求めていてsecret()
を使用している場合も同様に対応可能です。
CommandTest
class CommandTest extends TestCase
{
/**
* @see \MasterDataSeeder
*/
public function test001()
{
$command = \Mockery::mock("\App\Console\Commands\TestCommand[ask]");
$command->shouldReceive('ask')->once()->andReturn('山田太郎');
$command->setLaravel($this->app->make('Illuminate\Contracts\Foundation\Application'));
$command->setApplication($this->app->make('Symfony\Component\Console\Application'));
$tester = new CommandTester($command);
$tester->execute([]);
$this->assertSame("あなたの名前は山田太郎です。" . PHP_EOL, $tester->getDisplay());
}
}