-
Notifications
You must be signed in to change notification settings - Fork 11.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix doesntExpectOutput() always causing a failed test (#36806)
On what should be a passing test when the doesntExpectOutput() arg hasn't been output, PHPUnit's tearDown() call to Mockery::close() will throw any exception. Mockery\Exception\InvalidCountException: Method doWrite($output, <Any>) from Mockery_0_Symfony_Component_Console_Output_BufferedOutput should be called exactly 1 times but called 0 times. During a successful test run, it _should_ be called 0 times. Remove once() to allow the `andReturnUsing()` callback to be invoked one or many times. And add integration test coverage to PendingCommand.
- Loading branch information
Showing
2 changed files
with
132 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
<?php | ||
|
||
namespace Illuminate\Tests\Integration\Testing; | ||
|
||
use Illuminate\Support\Facades\Artisan; | ||
use Mockery; | ||
use Mockery\Exception\InvalidCountException; | ||
use Mockery\Exception\InvalidOrderException; | ||
use Orchestra\Testbench\TestCase; | ||
use PHPUnit\Framework\AssertionFailedError; | ||
|
||
class ArtisanCommandTest extends TestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
Artisan::command('survey', function () { | ||
$name = $this->ask('What is your name?'); | ||
|
||
$language = $this->choice('Which language do you prefer?', [ | ||
'PHP', | ||
'Ruby', | ||
'Python', | ||
]); | ||
|
||
$this->line("Your name is $name and you prefer $language."); | ||
}); | ||
|
||
Artisan::command('slim', function () { | ||
$this->line($this->ask('Who?')); | ||
$this->line($this->ask('What?')); | ||
$this->line($this->ask('Huh?')); | ||
}); | ||
} | ||
|
||
public function test_console_command_that_passes() | ||
{ | ||
$this->artisan('survey') | ||
->expectsQuestion('What is your name?', 'Taylor Otwell') | ||
->expectsQuestion('Which language do you prefer?', 'PHP') | ||
->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') | ||
->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') | ||
->assertExitCode(0); | ||
} | ||
|
||
public function test_console_command_that_passes_with_repeating_output() | ||
{ | ||
$this->artisan('slim') | ||
->expectsQuestion('Who?', 'Taylor') | ||
->expectsQuestion('What?', 'Taylor') | ||
->expectsQuestion('Huh?', 'Taylor') | ||
->expectsOutput('Taylor') | ||
->doesntExpectOutput('Otwell') | ||
->expectsOutput('Taylor') | ||
->expectsOutput('Taylor') | ||
->assertExitCode(0); | ||
} | ||
|
||
public function test_console_command_that_fails_from_unexpected_output() | ||
{ | ||
$this->expectException(AssertionFailedError::class); | ||
$this->expectExceptionMessage('Output "Your name is Taylor Otwell and you prefer PHP." was printed.'); | ||
|
||
$this->artisan('survey') | ||
->expectsQuestion('What is your name?', 'Taylor Otwell') | ||
->expectsQuestion('Which language do you prefer?', 'PHP') | ||
->doesntExpectOutput('Your name is Taylor Otwell and you prefer PHP.') | ||
->assertExitCode(0); | ||
} | ||
|
||
public function test_console_command_that_fails_from_missing_output() | ||
{ | ||
$this->expectException(AssertionFailedError::class); | ||
$this->expectExceptionMessage('Output "Your name is Taylor Otwell and you prefer PHP." was not printed.'); | ||
|
||
$this->ignoringMockOnceExceptions(function () { | ||
$this->artisan('survey') | ||
->expectsQuestion('What is your name?', 'Taylor Otwell') | ||
->expectsQuestion('Which language do you prefer?', 'Ruby') | ||
->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') | ||
->assertExitCode(0); | ||
}); | ||
} | ||
|
||
public function test_console_command_that_fails_from_exit_code_mismatch() | ||
{ | ||
$this->expectException(AssertionFailedError::class); | ||
$this->expectExceptionMessage('Expected status code 1 but received 0.'); | ||
|
||
$this->artisan('survey') | ||
->expectsQuestion('What is your name?', 'Taylor Otwell') | ||
->expectsQuestion('Which language do you prefer?', 'PHP') | ||
->assertExitCode(1); | ||
} | ||
|
||
public function test_console_command_that_fails_from_unordered_output() | ||
{ | ||
$this->expectException(InvalidOrderException::class); | ||
|
||
$this->ignoringMockOnceExceptions(function () { | ||
$this->artisan('slim') | ||
->expectsQuestion('Who?', 'Taylor') | ||
->expectsQuestion('What?', 'Danger') | ||
->expectsQuestion('Huh?', 'Otwell') | ||
->expectsOutput('Taylor') | ||
->expectsOutput('Otwell') | ||
->expectsOutput('Danger') | ||
->assertExitCode(0); | ||
}); | ||
} | ||
|
||
/** | ||
* Don't allow Mockery's InvalidCountException to be reported. Mocks setup | ||
* in PendingCommand cause PHPUnit tearDown() to later throw the exception. | ||
* | ||
* @param callable $callback | ||
* @return void | ||
*/ | ||
protected function ignoringMockOnceExceptions(callable $callback) | ||
{ | ||
try { | ||
$callback(); | ||
} finally { | ||
try { | ||
Mockery::close(); | ||
} catch (InvalidCountException $e) { | ||
// Ignore mock exception from PendingCommand::expectsOutput(). | ||
} | ||
} | ||
} | ||
} |