Skip to content

Commit

Permalink
[11.x] Allows asserting no output in Artisan commands (#50702)
Browse files Browse the repository at this point in the history
* Allows to test for no output console output

* Apply fixes from StyleCI

* Update InteractsWithConsole.php

---------

Co-authored-by: StyleCI Bot <bot@styleci.io>
Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
3 people committed Mar 27, 2024
1 parent 488e251 commit 4b4d5ad
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
32 changes: 28 additions & 4 deletions src/Illuminate/Testing/PendingCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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()
Expand Down
117 changes: 117 additions & 0 deletions tests/Integration/Testing/ArtisanCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 () {
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 4b4d5ad

Please sign in to comment.