diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php index c5d4e1aae60c..9c5060b3db54 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php @@ -15,6 +15,13 @@ trait InteractsWithConsole */ public $mockConsoleOutput = true; + /** + * Indicates if the command is expected to output anything. + * + * @var bool|null + */ + public $expectsOutput; + /** * All of the expected output lines. * diff --git a/src/Illuminate/Testing/PendingCommand.php b/src/Illuminate/Testing/PendingCommand.php index d2f75737e510..bf6a3edd5ad2 100644 --- a/src/Illuminate/Testing/PendingCommand.php +++ b/src/Illuminate/Testing/PendingCommand.php @@ -132,11 +132,17 @@ public function expectsChoice($question, $answer, $answers, $strict = false) /** * Specify output that should be printed when the command runs. * - * @param string $output + * @param string|null $output * @return $this */ - public function expectsOutput($output) + public function expectsOutput($output = null) { + if ($output === null) { + $this->test->expectsOutput = true; + + return $this; + } + $this->test->expectedOutput[] = $output; return $this; @@ -145,11 +151,17 @@ public function expectsOutput($output) /** * Specify output that should never be printed when the command runs. * - * @param string $output + * @param string|null $output * @return $this */ - public function doesntExpectOutput($output) + public function doesntExpectOutput($output = null) { + if ($output === null) { + $this->test->expectsOutput = false; + + return $this; + } + $this->test->unexpectedOutput[$output] = false; return $this; @@ -410,6 +422,18 @@ private function createABufferedOutputMock() ->shouldAllowMockingProtectedMethods() ->shouldIgnoreMissing(); + if ($this->test->expectsOutput === false) { + $mock->shouldReceive('doWrite')->never(); + + return $mock; + } + + if ($this->test->expectsOutput === true + && count($this->test->expectedOutput) === 0 + && count($this->test->expectedOutputSubstrings) === 0) { + $mock->shouldReceive('doWrite')->atLeast()->once(); + } + foreach ($this->test->expectedOutput as $i => $output) { $mock->shouldReceive('doWrite') ->once() diff --git a/tests/Integration/Testing/ArtisanCommandTest.php b/tests/Integration/Testing/ArtisanCommandTest.php index 1592d63dfea8..08cd327e0582 100644 --- a/tests/Integration/Testing/ArtisanCommandTest.php +++ b/tests/Integration/Testing/ArtisanCommandTest.php @@ -2,6 +2,7 @@ namespace Illuminate\Tests\Integration\Testing; +use Illuminate\Console\Command; use Illuminate\Support\Facades\Artisan; use Mockery as m; use Mockery\Exception\InvalidCountException; @@ -33,6 +34,22 @@ protected function setUp(): void $this->line($this->ask('Huh?')); }); + Artisan::command('interactions', function () { + /** @var Command $this */ + $this->ask('What is your name?'); + $this->choice('Which language do you prefer?', [ + 'PHP', + 'PHP', + 'PHP', + ]); + + $this->table(['Name', 'Email'], [ + ['Taylor Otwell', 'taylor@laravel.com'], + ]); + + $this->confirm('Do you want to continue?', true); + }); + Artisan::command('exit {code}', fn () => (int) $this->argument('code')); Artisan::command('contains', function () { @@ -146,6 +163,106 @@ public function test_console_command_that_passes_if_the_output_contains() ->assertExitCode(0); } + public function test_console_command_that_passes_if_outputs_something() + { + $this->artisan('contains') + ->expectsOutput() + ->assertExitCode(0); + } + + public function test_console_command_that_passes_if_outputs_is_something_and_is_the_expected_output() + { + $this->artisan('contains') + ->expectsOutput() + ->expectsOutput('My name is Taylor Otwell') + ->assertExitCode(0); + } + + public function test_console_command_that_fail_if_doesnt_output_something() + { + $this->expectException(InvalidCountException::class); + + $this->artisan('exit', ['code' => 0]) + ->expectsOutput() + ->assertExitCode(0); + + m::close(); + } + + public function test_console_command_that_fail_if_doesnt_output_something_and_is_not_the_expected_output() + { + $this->expectException(AssertionFailedError::class); + + $this->ignoringMockOnceExceptions(function () { + $this->artisan('exit', ['code' => 0]) + ->expectsOutput() + ->expectsOutput('My name is Taylor Otwell') + ->assertExitCode(0); + }); + } + + public function test_console_command_that_passes_if_does_not_output_anything() + { + $this->artisan('exit', ['code' => 0]) + ->doesntExpectOutput() + ->assertExitCode(0); + } + + public function test_console_command_that_passes_if_does_not_output_anything_and_is_not_the_expected_output() + { + $this->artisan('exit', ['code' => 0]) + ->doesntExpectOutput() + ->doesntExpectOutput('My name is Taylor Otwell') + ->assertExitCode(0); + } + + public function test_console_command_that_passes_if_expects_output_and_there_is_interactions() + { + $this->artisan('interactions', ['--no-interaction' => true]) + ->expectsOutput() + ->expectsQuestion('What is your name?', 'Taylor Otwell') + ->expectsChoice('Which language do you prefer?', 'PHP', ['PHP', 'PHP', 'PHP']) + ->expectsConfirmation('Do you want to continue?', true) + ->assertExitCode(0); + } + + public function test_console_command_that_fails_if_doesnt_expect_output_but__there_is_interactions() + { + $this->expectException(InvalidCountException::class); + + $this->artisan('interactions', ['--no-interaction' => true]) + ->doesntExpectOutput() + ->expectsQuestion('What is your name?', 'Taylor Otwell') + ->expectsChoice('Which language do you prefer?', 'PHP', ['PHP', 'PHP', 'PHP']) + ->expectsConfirmation('Do you want to continue?', true) + ->assertExitCode(0); + + m::close(); + } + + public function test_console_command_that_fails_if_doesnt_expect_output_but_outputs_something() + { + $this->expectException(InvalidCountException::class); + + $this->artisan('contains') + ->doesntExpectOutput() + ->assertExitCode(0); + + m::close(); + } + + public function test_console_command_that_fails_if_doesnt_expect_output_and_does_expect_output() + { + $this->expectException(InvalidCountException::class); + + $this->artisan('contains') + ->doesntExpectOutput() + ->doesntExpectOutput('My name is Taylor Otwell') + ->assertExitCode(0); + + m::close(); + } + public function test_console_command_that_fails_if_the_output_does_not_contain() { $this->expectException(AssertionFailedError::class);