简体   繁体   中英

How To Test Artisan Commands with ask() in Laravel 5.4

Trying to write a test for laravel php artisan command with ask() function. I have never used mockery before, but when i try to run test, it freezes, so i guess, i'm doing something wrong.

MyCommand.php:

public function handle()
    {
        $input['answer1'] = $this->ask('Ask question 1');
        $input['answer2'] = $this->ask('Ask question 2');
        $input['answer3'] = $this->ask('Ask question 3');


        //--- processing validation        
        $validator = Validator::make($input, [
            'answer1' => 'required',
            'answer2' => 'required',
            'answer3' => 'required',
            
        ]);
        
        if ($validator->fails()) {
            // processing error
            }
        } else {
            // saving to DB
        }
    }

And my unit test:

    $command = m::mock('\App\Console\Commands\Questions');
            
            
    $command->shouldReceive('ask')              
            ->andReturn('Answer 1')
            ->shouldReceive('ask')
            ->andReturn('Answer 2')
            ->shouldReceive('ask')
            ->andReturn('Answer 3')
    
            
    $this->artisan('myCommand:toRun');

    $this->assertDatabaseHas('myTable', [
           'question1' => 'answer1'
    ]);

Laravel 5.4 - 5.6

The actual issue here is that running the console command is waiting for user input, however we are running this through PHPUnit so we are unable to enter anything.

Bumping up against limitations in unit testing can initially be frustrating, however limitations you find can end up being a blessing in disguise.

Currently, your implementation is tightly coupled to a view (a console command, so a view to an admin, but still a view none-the-less.) What could be done here is place any logic within a separate class which MyCommand can utilize, and which PHPUnit can actually test on their own. We know that the fundamentals of running a custom command work, as demonstrated in Laravel unit tests, so we can offload our logic in a separate, testable class.

Your new class might look something like this:

class CommandLogic
{

    public function getQuestion1Text()
    {
        return 'Ask question 1';
    }

    public function getQuestion2Text()
    {
        return 'Ask question 2';
    }

    public function getQuestion3Text()
    {
        return 'Ask question 3';
    }

    public function submit(array $input)
    {
        $validator = \Illuminate\Support\Facades\Validator::make($input, [
            'answer1' => 'required',
            'answer2' => 'required',
            'answer3' => 'required',
        ]);

        if ($validator->fails()) {
            // processing error
        } else {
            // saving to DB
        }
    }

}

...your actual unit test, something like this:

$commandLogic = new CommandLogic();
$sampleInput = [
    'answer1' => 'test1',
    'answer2' => 'test2',
    'answer3' => 'test3',
];

$commandLogic->submit($sampleInput);
$this->assertDatabaseHas('myTable', [
    'question1' => 'test1'
]);

...and your console command, something like this:

public function handle()
{
    $commandLogic = new CommandLogic();
    $input['answer1'] = $this->ask($commandLogic->getQuestion1Text());
    $input['answer2'] = $this->ask($commandLogic->getQuestion2Text());
    $input['answer3'] = $this->ask($commandLogic->getQuestion3Text());

    $commandLogic->submit($input);
}

This enforces the single responsibility principle and separates the moving pieces in your codebase. I know this may feel like a bit of a cop out, but testing this stuff in Laravel 5.4 is tough. If you are willing to upgrade to 5.7 or higher, read below...


Laravel 5.7+

Laravel 5.7 introduced being able to run console tests, which satisfies the exact requirement this question is asking - https://laravel.com/docs/5.7/console-tests . This is more of a full integration test rather than a unit test.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM