diff --git a/src/Test/LegacyWebTestCase.php b/src/Test/LegacyWebTestCase.php new file mode 100644 index 00000000..1c196a3e --- /dev/null +++ b/src/Test/LegacyWebTestCase.php @@ -0,0 +1,62 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Liip\FunctionalTestBundle\Test; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as SymfonyWebTestCase; +use Symfony\Component\Console\Tester\CommandTester; + +abstract class LegacyWebTestCase extends SymfonyWebTestCase +{ + /** + * Runs a command and returns a CommandTester. + */ + protected function runCommand(string $name, array $params = [], bool $reuseKernel = false): CommandTester + { + if (!$reuseKernel) { + if (null !== static::$kernel) { + static::ensureKernelShutdown(); + } + + $kernel = static::$kernel = static::createKernel(['environment' => $this->environment ?? static::$env]); + $kernel->boot(); + } else { + $kernel = $this->getContainer()->get('kernel'); + } + + $application = new Application($kernel); + + $options = [ + 'interactive' => false, + 'decorated' => $this->getDecorated(), + 'verbosity' => $this->getVerbosityLevel(), + ]; + + $command = $application->find($name); + $commandTester = new CommandTester($command); + + if (null !== $inputs = $this->getInputs()) { + $commandTester->setInputs($inputs); + $options['interactive'] = true; + $this->inputs = null; + } + + $commandTester->execute( + array_merge(['command' => $command->getName()], $params), + $options + ); + + return $commandTester; + } +} diff --git a/src/Test/ModernWebTestCase.php b/src/Test/ModernWebTestCase.php new file mode 100644 index 00000000..c9002bc6 --- /dev/null +++ b/src/Test/ModernWebTestCase.php @@ -0,0 +1,55 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Liip\FunctionalTestBundle\Test; + +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as SymfonyWebTestCase; +use Symfony\Component\Console\Tester\ExecutionResult; + +abstract class ModernWebTestCase extends SymfonyWebTestCase +{ + /** + * Runs a console command and returns the execution result. + */ + public static function runCommand( + string $name, + array $input = [], + mixed $interactiveInputs = [], + ?bool $interactive = null, + ?bool $decorated = null, + ?int $verbosity = null, + array $normalizers = [] + ): ExecutionResult { + if (\is_bool($interactiveInputs)) { + $reuseKernel = $interactiveInputs; + if (!$reuseKernel) { + static::ensureKernelShutdown(); + } + $interactiveInputs = []; + } + + // Retrieve properties from the active test instance if not explicitly provided + if (null === $decorated && WebTestCase::$activeInstance) { + $decorated = WebTestCase::$activeInstance->getDecorated(); + } + if (null === $verbosity && WebTestCase::$activeInstance) { + $verbosity = WebTestCase::$activeInstance->getVerbosityLevel(); + } + if (empty($interactiveInputs) && WebTestCase::$activeInstance) { + $interactiveInputs = WebTestCase::$activeInstance->getInputs() ?? []; + WebTestCase::$activeInstance->inputs = null; + } + + return parent::runCommand($name, $input, $interactiveInputs, $interactive, $decorated, $verbosity, $normalizers); + } +} diff --git a/src/Test/WebTestCase.php b/src/Test/WebTestCase.php index 16025579..8cea336f 100644 --- a/src/Test/WebTestCase.php +++ b/src/Test/WebTestCase.php @@ -16,11 +16,8 @@ use Liip\FunctionalTestBundle\Utils\HttpAssertions; use PHPUnit\Framework\MockObject\MockBuilder; use Symfony\Bundle\FrameworkBundle\Client; -use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\KernelBrowser; -use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase; use Symfony\Component\BrowserKit\Cookie; -use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ResettableContainerInterface; use Symfony\Component\DomCrawler\Crawler; @@ -40,6 +37,16 @@ class_alias(KernelBrowser::class, Client::class); } +if (!method_exists(\Symfony\Bundle\FrameworkBundle\Test\WebTestCase::class, 'runCommand')) { + if (!class_exists(BaseWebTestCase::class, false)) { + class_alias(LegacyWebTestCase::class, BaseWebTestCase::class); + } +} else { + if (!class_exists(BaseWebTestCase::class, false)) { + class_alias(ModernWebTestCase::class, BaseWebTestCase::class); + } +} + /** * @author Lea Haensenberger * @author Lukas Kahwe Smith @@ -51,6 +58,8 @@ class_alias(KernelBrowser::class, Client::class); */ abstract class WebTestCase extends BaseWebTestCase { + public static ?self $activeInstance = null; + protected static $env = 'test'; protected $containers; @@ -66,7 +75,7 @@ abstract class WebTestCase extends BaseWebTestCase /** * @var array|null */ - private $inputs; + protected $inputs; /** * @var array @@ -103,47 +112,6 @@ protected function setServiceMock( } } - /** - * Builds up the environment to run the given command. - */ - protected function runCommand(string $name, array $params = [], bool $reuseKernel = false): CommandTester - { - if (!$reuseKernel) { - if (null !== static::$kernel) { - static::ensureKernelShutdown(); - } - - $kernel = static::bootKernel(['environment' => static::$env]); - $kernel->boot(); - } else { - $kernel = $this->getContainer()->get('kernel'); - } - - $application = new Application($kernel); - - $options = [ - 'interactive' => false, - 'decorated' => $this->getDecorated(), - 'verbosity' => $this->getVerbosityLevel(), - ]; - - $command = $application->find($name); - $commandTester = new CommandTester($command); - - if (null !== $inputs = $this->getInputs()) { - $commandTester->setInputs($inputs); - $options['interactive'] = true; - $this->inputs = null; - } - - $commandTester->execute( - array_merge(['command' => $command->getName()], $params), - $options - ); - - return $commandTester; - } - /** * Retrieves the output verbosity level. * @@ -535,8 +503,17 @@ public static function assertValidationErrors(array $expected, ContainerInterfac HttpAssertions::assertValidationErrors($expected, $container); } + protected function setUp(): void + { + parent::setUp(); + + self::$activeInstance = $this; + } + protected function tearDown(): void { + self::$activeInstance = null; + if (null !== $this->containers) { foreach ($this->containers as $container) { if ($container instanceof ResettableContainerInterface) { diff --git a/tests/Command/CommandConfigTest.php b/tests/Command/CommandConfigTest.php index ab231a2c..66126a3e 100644 --- a/tests/Command/CommandConfigTest.php +++ b/tests/Command/CommandConfigTest.php @@ -47,7 +47,11 @@ public function testRunCommand(): void // Run command without options $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test'); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); + if ($this->commandTester instanceof CommandTester) { + $this->assertInstanceOf(CommandTester::class, $this->commandTester); + } else { + $this->assertInstanceOf(\Symfony\Component\Console\Tester\ExecutionResult::class, $this->commandTester); + } // Test values from configuration $this->assertStringContainsString('Environment: test', $this->commandTester->getDisplay()); diff --git a/tests/Command/CommandTest.php b/tests/Command/CommandTest.php index c0e2b472..a221a687 100644 --- a/tests/Command/CommandTest.php +++ b/tests/Command/CommandTest.php @@ -34,7 +34,9 @@ public function testRunCommandWithoutOptionsAndReuseKernel(): void // Test default values $this->assertStringContainsString('Environment: test', $this->commandTester->getDisplay()); $this->assertStringContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); - $this->assertFalse($this->commandTester->getInput()->isInteractive()); + if ($this->commandTester instanceof CommandTester) { + $this->assertFalse($this->commandTester->getInput()->isInteractive()); + } $this->assertIsBool($this->getDecorated()); $this->assertTrue($this->getDecorated()); @@ -42,8 +44,8 @@ public function testRunCommandWithoutOptionsAndReuseKernel(): void // Run command and reuse kernel $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test', [], true); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); - $this->assertSame(0, $this->commandTester->getStatusCode()); + $this->assertCommandResultType($this->commandTester); + $this->assertSame(0, $this->getStatusCode($this->commandTester)); $this->assertStringContainsString('Environment: test', $this->commandTester->getDisplay()); $this->assertStringContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); @@ -57,7 +59,9 @@ public function testRunCommandWithInputs(): void $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test:interactive'); $this->assertNull($this->getInputs()); - $this->assertTrue($this->commandTester->getInput()->isInteractive()); + if ($this->commandTester instanceof CommandTester) { + $this->assertTrue($this->commandTester->getInput()->isInteractive()); + } $this->assertStringContainsString('Value of answer: foo', $this->commandTester->getDisplay()); // Run command again @@ -66,7 +70,9 @@ public function testRunCommandWithInputs(): void $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test:interactive'); $this->assertNull($this->getInputs()); - $this->assertFalse($this->commandTester->getInput()->isInteractive()); + if ($this->commandTester instanceof CommandTester) { + $this->assertFalse($this->commandTester->getInput()->isInteractive()); + } // The default value is shown $this->assertStringContainsString('Value of answer: AcmeDemoBundle', $this->commandTester->getDisplay()); } @@ -86,8 +92,8 @@ public function testRunCommandWithoutOptionsAndNotReuseKernel(bool $useEnv): voi // Run command without options $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test'); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); - $this->assertSame(0, $this->commandTester->getStatusCode()); + $this->assertCommandResultType($this->commandTester); + $this->assertSame(0, $this->getStatusCode($this->commandTester)); // Test default values $this->assertStringContainsString('Environment: test', $this->commandTester->getDisplay()); @@ -107,7 +113,7 @@ public function testRunCommandWithoutOptionsAndNotReuseKernel(bool $useEnv): voi $this->getContainer(); $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test', [], true); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); + $this->assertCommandResultType($this->commandTester); $this->assertStringContainsString('Environment: prod', $this->commandTester->getDisplay()); $this->assertStringContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); @@ -128,8 +134,8 @@ public function testRunCommandWithoutDecoration(): void $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test'); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); - $this->assertSame(0, $this->commandTester->getStatusCode()); + $this->assertCommandResultType($this->commandTester); + $this->assertSame(0, $this->getStatusCode($this->commandTester)); $this->assertStringContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); @@ -148,8 +154,8 @@ public function testRunCommandVerbosityQuiet(): void $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test'); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); - $this->assertSame(0, $this->commandTester->getStatusCode()); + $this->assertCommandResultType($this->commandTester); + $this->assertSame(0, $this->getStatusCode($this->commandTester)); $this->assertEmpty($this->commandTester->getDisplay()); $this->assertStringNotContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); @@ -168,9 +174,9 @@ public function testRunCommandVerbosityImplicitlyNormal(): void $this->assertFalse($this->getDecorated()); $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test'); - $this->assertSame(0, $this->commandTester->getStatusCode()); + $this->assertSame(0, $this->getStatusCode($this->commandTester)); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); + $this->assertCommandResultType($this->commandTester); $this->assertStringContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); $this->assertStringNotContainsString('Verbosity level: VERBOSE', $this->commandTester->getDisplay()); @@ -185,9 +191,9 @@ public function testRunCommandVerbosityExplicitlyNormal(): void $this->isDecorated(false); $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test'); - $this->assertSame(0, $this->commandTester->getStatusCode()); + $this->assertSame(0, $this->getStatusCode($this->commandTester)); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); + $this->assertCommandResultType($this->commandTester); $this->assertStringContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); $this->assertStringNotContainsString('Verbosity level: VERBOSE', $this->commandTester->getDisplay()); @@ -201,9 +207,9 @@ public function testRunCommandVerbosityVerbose(): void $this->assertSame(OutputInterface::VERBOSITY_VERBOSE, $this->getVerbosityLevel()); $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test'); - $this->assertSame(0, $this->commandTester->getStatusCode()); + $this->assertSame(0, $this->getStatusCode($this->commandTester)); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); + $this->assertCommandResultType($this->commandTester); $this->assertStringContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); $this->assertStringContainsString('Verbosity level: VERBOSE', $this->commandTester->getDisplay()); @@ -221,9 +227,9 @@ public function testRunCommandVerbosityVeryVerbose(): void $this->assertFalse($this->getDecorated()); $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test'); - $this->assertSame(0, $this->commandTester->getStatusCode()); + $this->assertSame(0, $this->getStatusCode($this->commandTester)); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); + $this->assertCommandResultType($this->commandTester); $this->assertStringContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); $this->assertStringContainsString('Verbosity level: VERBOSE', $this->commandTester->getDisplay()); @@ -241,9 +247,9 @@ public function testRunCommandVerbosityDebug(): void $this->assertFalse($this->getDecorated()); $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test'); - $this->assertSame(0, $this->commandTester->getStatusCode()); + $this->assertSame(0, $this->getStatusCode($this->commandTester)); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); + $this->assertCommandResultType($this->commandTester); $this->assertStringContainsString('Verbosity level: NORMAL', $this->commandTester->getDisplay()); $this->assertStringContainsString('Verbosity level: VERBOSE', $this->commandTester->getDisplay()); @@ -255,9 +261,9 @@ public function testRunCommandStatusCode(): void { $this->commandTester = $this->runCommand('liipfunctionaltestbundle:test-status-code'); - $this->assertInstanceOf(CommandTester::class, $this->commandTester); + $this->assertCommandResultType($this->commandTester); - $this->assertSame(10, $this->commandTester->getStatusCode()); + $this->assertSame(10, $this->getStatusCode($this->commandTester)); } public function testRunCommandVerbosityOutOfBound(): void @@ -269,6 +275,20 @@ public function testRunCommandVerbosityOutOfBound(): void $this->runCommand('liipfunctionaltestbundle:test'); } + private function assertCommandResultType($result): void + { + if ($result instanceof CommandTester) { + $this->assertInstanceOf(CommandTester::class, $result); + } else { + $this->assertInstanceOf(\Symfony\Component\Console\Tester\ExecutionResult::class, $result); + } + } + + private function getStatusCode($result): int + { + return ($result instanceof CommandTester) ? $result->getStatusCode() : $result->statusCode; + } + protected function tearDown(): void { parent::tearDown();