diff --git a/CHANGELOG.md b/CHANGELOG.md index ba4aa5a1a..e2d420841 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ +1.29 +==== + +* [make:test] Added the maker +* [make:unit-test] Deprecated the maker +* [make:functional-test] Deprecated the maker + 1.27 +==== * [make:registration-form] Added a new question to generate code that will allow users to click on the "verify email" link in their email without needing to be diff --git a/src/DependencyInjection/CompilerPass/MakeCommandRegistrationPass.php b/src/DependencyInjection/CompilerPass/MakeCommandRegistrationPass.php index cd7a2f85a..2b99e7adb 100644 --- a/src/DependencyInjection/CompilerPass/MakeCommandRegistrationPass.php +++ b/src/DependencyInjection/CompilerPass/MakeCommandRegistrationPass.php @@ -29,6 +29,10 @@ public function process(ContainerBuilder $container) { foreach ($container->findTaggedServiceIds(self::MAKER_TAG) as $id => $tags) { $def = $container->getDefinition($id); + if ($def->isDeprecated()) { + continue; + } + $class = $container->getParameterBag()->resolveValue($def->getClass()); if (!is_subclass_of($class, MakerInterface::class)) { throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, MakerInterface::class)); @@ -50,6 +54,15 @@ public function process(ContainerBuilder $container) $commandDefinition->addTag('console.command', $tagAttributes); + /* + * @deprecated remove this block when removing make:unit-test and make:functional-test + */ + if (method_exists($class, 'getCommandAliases')) { + foreach ($class::getCommandAliases() as $alias) { + $commandDefinition->addTag('console.command', ['command' => $alias, 'description' => 'Deprecated alias of "make:test"']); + } + } + $container->setDefinition(sprintf('maker.auto_command.%s', Str::asTwigVariable($class::getCommandName())), $commandDefinition); } } diff --git a/src/DependencyInjection/MakerExtension.php b/src/DependencyInjection/MakerExtension.php index 8051d8919..a4de2f23f 100644 --- a/src/DependencyInjection/MakerExtension.php +++ b/src/DependencyInjection/MakerExtension.php @@ -15,6 +15,7 @@ use Symfony\Bundle\MakerBundle\MakerInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -25,6 +26,11 @@ */ class MakerExtension extends Extension { + /** + * @deprecated remove this block when removing make:unit-test and make:functional-test + */ + private const TEST_MAKER_DEPRECATION_MESSAGE = 'The "%service_id%" service is deprecated, use "maker.maker.make_test" instead.'; + /** * {@inheritdoc} */ @@ -34,6 +40,17 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('services.xml'); $loader->load('makers.xml'); + /** + * @deprecated remove this block when removing make:unit-test and make:functional-test + */ + $deprecParams = method_exists(Definition::class, 'getDeprecation') ? ['symfony/maker-bundle', '1.29', self::TEST_MAKER_DEPRECATION_MESSAGE] : [true, self::TEST_MAKER_DEPRECATION_MESSAGE]; + $container + ->getDefinition('maker.maker.make_unit_test') + ->setDeprecated(...$deprecParams); + $container + ->getDefinition('maker.maker.make_functional_test') + ->setDeprecated(...$deprecParams); + $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); diff --git a/src/Maker/MakeFunctionalTest.php b/src/Maker/MakeFunctionalTest.php index 2ea734486..2e20c1ffa 100644 --- a/src/Maker/MakeFunctionalTest.php +++ b/src/Maker/MakeFunctionalTest.php @@ -23,7 +23,11 @@ use Symfony\Component\CssSelector\CssSelectorConverter; use Symfony\Component\Panther\PantherTestCaseTrait; +trigger_deprecation('symfony/maker-bundle', '1.29', 'The "%s" class is deprecated, use "%s" instead.', MakeFunctionalTest::class, MakeTest::class); + /** + * @deprecated since MakerBundle 1.29, use Symfony\Bundle\MakerBundle\Maker\MakeTest instead. + * * @author Javier Eguiluz * @author Ryan Weaver */ diff --git a/src/Maker/MakeTest.php b/src/Maker/MakeTest.php new file mode 100644 index 000000000..5d9533f53 --- /dev/null +++ b/src/Maker/MakeTest.php @@ -0,0 +1,189 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MakerBundle\Maker; + +use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase; +use Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait; +use Symfony\Bundle\MakerBundle\ConsoleStyle; +use Symfony\Bundle\MakerBundle\DependencyBuilder; +use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; +use Symfony\Bundle\MakerBundle\Generator; +use Symfony\Bundle\MakerBundle\InputAwareMakerInterface; +use Symfony\Bundle\MakerBundle\InputConfiguration; +use Symfony\Component\BrowserKit\History; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\CssSelector\CssSelectorConverter; +use Symfony\Component\Panther\PantherTestCaseTrait; + +/** + * @author Kévin Dunglas + * @author Javier Eguiluz + * @author Ryan Weaver + */ +final class MakeTest extends AbstractMaker implements InputAwareMakerInterface +{ + private const DESCRIPTIONS = [ + 'TestCase' => 'basic PHPUnit tests', + 'KernelTestCase' => 'basic tests that have access to Symfony services', + 'WebTestCase' => 'to run browser-like scenarios, but that don\'t execute JavaScript code', + 'ApiTestCase' => 'to run API-oriented scenarios', + 'PantherTestCase' => 'to run e2e scenarios, using a real-browser or HTTP client and a real web server', + ]; + private const DOCS = [ + 'TestCase' => 'https://symfony.com/doc/current/testing.html#unit-tests', + 'KernelTestCase' => 'https://symfony.com/doc/current/testing/database.html#functional-testing-of-a-doctrine-repository', + 'WebTestCase' => 'https://symfony.com/doc/current/testing.html#functional-tests', + 'ApiTestCase' => 'https://api-platform.com/docs/distribution/testing/', + 'PantherTestCase' => 'https://github.com/symfony/panther#testing-usage', + ]; + + public static function getCommandName(): string + { + return 'make:test'; + } + + /** + * @deprecated remove this method when removing make:unit-test and make:functional-test + */ + public static function getCommandAliases(): iterable + { + yield 'make:unit-test'; + yield 'make:functional-test'; + } + + public static function getCommandDescription(): string + { + return 'Creates a new test class'; + } + + public function configureCommand(Command $command, InputConfiguration $inputConf) + { + $typesDesc = []; + $typesHelp = []; + foreach (self::DESCRIPTIONS as $type => $desc) { + $typesDesc[] = sprintf('%s (%s)', $type, $desc); + $typesHelp[] = sprintf('* %s: %s', $type, $desc); + } + + $command + ->addArgument('name', InputArgument::OPTIONAL, 'The name of the test class (e.g. BlogPostTest)') + ->addArgument('type', InputArgument::OPTIONAL, 'The type of test: '.implode(', ', $typesDesc)) + ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeTest.txt').implode("\n", $typesHelp)); + + $inputConf->setArgumentAsNonInteractive('type'); + } + + public function interact(InputInterface $input, ConsoleStyle $io, Command $command) + { + /* @deprecated remove the following block when removing make:unit-test and make:functional-test */ + $currentCommand = $input->getFirstArgument(); + switch ($currentCommand) { + case 'make:unit-test': + $input->setArgument('type', 'TestCase'); + $io->caution('The "make:unit-test" command is deprecated, use "make:test" instead.'); + + return; + + case 'make:functional-test': + $input->setArgument('type', trait_exists(PantherTestCaseTrait::class) ? 'WebTestCase' : 'PantherTestCase'); + $io->caution('The "make:functional-test" command is deprecated, use "make:test" instead.'); + + return; + } + + if (null !== $type = $input->getArgument('type')) { + if (!isset(self::DESCRIPTIONS[$type])) { + throw new RuntimeCommandException(sprintf('The test type must be one of "%s", "%s" given.', implode('", "', array_keys(self::DESCRIPTIONS)), $type)); + } + + return; + } + + $input->setArgument( + 'type', + $io->choice('Which test type would you like?', self::DESCRIPTIONS) + ); + } + + public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator) + { + $testClassNameDetails = $generator->createClassNameDetails( + $input->getArgument('name'), + 'Tests\\', + 'Test' + ); + + $type = $input->getArgument('type'); + + $generator->generateClass( + $testClassNameDetails->getFullName(), + "test/$type.tpl.php", + ['web_assertions_are_available' => trait_exists(WebTestAssertionsTrait::class)] + ); + + $generator->writeChanges(); + + $this->writeSuccessMessage($io); + + $io->text([ + 'Next: Open your new test class and start customizing it.', + sprintf('Find the documentation at %s', self::DOCS[$type]), + ]); + } + + public function configureDependencies(DependencyBuilder $dependencies, InputInterface $input = null): void + { + if (null === $input) { + return; + } + + switch ($input->getArgument('type')) { + case 'WebTestCase': + $dependencies->addClassDependency( + History::class, + 'browser-kit', + true, + true + ); + $dependencies->addClassDependency( + CssSelectorConverter::class, + 'css-selector', + true, + true + ); + + return; + + case 'ApiTestCase': + $dependencies->addClassDependency( + ApiTestCase::class, + 'api', + true, + false + ); + + return; + + case 'PantherTestCase': + $dependencies->addClassDependency( + PantherTestCaseTrait::class, + 'panther', + true, + true + ); + + return; + } + } +} diff --git a/src/Maker/MakeUnitTest.php b/src/Maker/MakeUnitTest.php index 4aa4ecb4a..8ff52fbb3 100644 --- a/src/Maker/MakeUnitTest.php +++ b/src/Maker/MakeUnitTest.php @@ -19,7 +19,11 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +trigger_deprecation('symfony/maker-bundle', '1.29', 'The "%s" class is deprecated, use "%s" instead.', MakeUnitTest::class, MakeTest::class); + /** + * @deprecated since MakerBundle 1.29, use Symfony\Bundle\MakerBundle\Maker\MakeTest instead. + * * @author Javier Eguiluz * @author Ryan Weaver */ diff --git a/src/Resources/config/makers.xml b/src/Resources/config/makers.xml index 6363dd00e..2dd5d1d56 100644 --- a/src/Resources/config/makers.xml +++ b/src/Resources/config/makers.xml @@ -99,6 +99,10 @@ + + + + diff --git a/src/Resources/help/MakeTest.txt b/src/Resources/help/MakeTest.txt new file mode 100644 index 000000000..091be34d1 --- /dev/null +++ b/src/Resources/help/MakeTest.txt @@ -0,0 +1,7 @@ +The %command.name% command generates a new test class. + +php %command.full_name% BlogPostTest TestCase + +If the first argument is missing, the command will ask for the class name interactively. + +If the second argument is missing, the command will ask for the test type interactively. diff --git a/src/Resources/skeleton/test/ApiTestCase.tpl.php b/src/Resources/skeleton/test/ApiTestCase.tpl.php new file mode 100644 index 000000000..48d354505 --- /dev/null +++ b/src/Resources/skeleton/test/ApiTestCase.tpl.php @@ -0,0 +1,16 @@ + + +namespace ; + +use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase; + +class extends ApiTestCase +{ + public function testSomething(): void + { + $response = static::createClient()->request('GET', '/'); + + $this->assertResponseIsSuccessful(); + $this->assertJsonContains(['@id' => '/']); + } +} diff --git a/src/Resources/skeleton/test/Functional.tpl.php b/src/Resources/skeleton/test/Functional.tpl.php index 7e76354e2..6e41411e3 100644 --- a/src/Resources/skeleton/test/Functional.tpl.php +++ b/src/Resources/skeleton/test/Functional.tpl.php @@ -1,3 +1,4 @@ + namespace ; @@ -10,16 +11,24 @@ class extends { - public function testSomething() + public function testSomething(): void { + + $client = static::createPantherClient(); + $client = static::createClient(); + $crawler = $client->request('GET', '/'); + $this->assertResponseIsSuccessful(); + $this->assertSelectorTextContains('h1', 'Hello World'); + $this->assertSame(200, $client->getResponse()->getStatusCode()); + $this->assertStringContainsString('Hello World', $crawler->filter('h1')->text()); } diff --git a/src/Resources/skeleton/test/KernelTestCase.tpl.php b/src/Resources/skeleton/test/KernelTestCase.tpl.php new file mode 100644 index 000000000..9dd3b9def --- /dev/null +++ b/src/Resources/skeleton/test/KernelTestCase.tpl.php @@ -0,0 +1,15 @@ + + +namespace ; + +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; + +class extends KernelTestCase +{ + public function testSomething(): void + { + $kernel = self::bootKernel(); + + $this->assertSame('test', $kernel->getEnvironment()); + } +} diff --git a/src/Resources/skeleton/test/PantherTestCase.tpl.php b/src/Resources/skeleton/test/PantherTestCase.tpl.php new file mode 100644 index 000000000..ab42fdf95 --- /dev/null +++ b/src/Resources/skeleton/test/PantherTestCase.tpl.php @@ -0,0 +1,20 @@ + + +namespace ; + +use Symfony\Component\Panther\PantherTestCase; + +class extends PantherTestCase +{ + public function testSomething(): void + { + $client = static::createPantherClient(); + $crawler = $client->request('GET', '/'); + + + $this->assertSelectorTextContains('h1', 'Hello World'); + + $this->assertStringContainsString('Hello World', $crawler->filter('h1')->text()); + + } +} diff --git a/src/Resources/skeleton/test/TestCase.tpl.php b/src/Resources/skeleton/test/TestCase.tpl.php new file mode 100644 index 000000000..e4a9ad644 --- /dev/null +++ b/src/Resources/skeleton/test/TestCase.tpl.php @@ -0,0 +1,13 @@ + + +namespace ; + +use PHPUnit\Framework\TestCase; + +class extends TestCase +{ + public function testSomething(): void + { + $this->assertTrue(true); + } +} diff --git a/src/Resources/skeleton/test/Unit.tpl.php b/src/Resources/skeleton/test/Unit.tpl.php index e121e96c9..35b3140f4 100644 --- a/src/Resources/skeleton/test/Unit.tpl.php +++ b/src/Resources/skeleton/test/Unit.tpl.php @@ -1,3 +1,4 @@ + namespace ; @@ -6,7 +7,7 @@ class extends TestCase { - public function testSomething() + public function testSomething(): void { $this->assertTrue(true); } diff --git a/src/Resources/skeleton/test/WebTestCase.tpl.php b/src/Resources/skeleton/test/WebTestCase.tpl.php new file mode 100644 index 000000000..dcd2386df --- /dev/null +++ b/src/Resources/skeleton/test/WebTestCase.tpl.php @@ -0,0 +1,22 @@ + + +namespace ; + +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + +class extends WebTestCase +{ + public function testSomething(): void + { + $client = static::createClient(); + $crawler = $client->request('GET', '/'); + + + $this->assertResponseIsSuccessful(); + $this->assertSelectorTextContains('h1', 'Hello World'); + + $this->assertSame(200, $client->getResponse()->getStatusCode()); + $this->assertStringContainsString('Hello World', $crawler->filter('h1')->text()); + + } +} diff --git a/tests/Maker/FunctionalTest.php b/tests/Maker/FunctionalTest.php index 4b7ac60b2..066eb9880 100644 --- a/tests/Maker/FunctionalTest.php +++ b/tests/Maker/FunctionalTest.php @@ -30,7 +30,11 @@ public function testWiring() $kernel = new MakerTestKernel('dev', true); $finder = new Finder(); - $finder->in(__DIR__.'/../../src/Maker'); + $finder + ->in(__DIR__.'/../../src/Maker') + // exclude deprecated classes + ->notContains('/@deprecated/') + ; $application = new Application($kernel); foreach ($finder as $file) { diff --git a/tests/Maker/MakeFunctionalTestTest.php b/tests/Maker/MakeFunctionalTestTest.php index ecb134452..7e5714a10 100644 --- a/tests/Maker/MakeFunctionalTestTest.php +++ b/tests/Maker/MakeFunctionalTestTest.php @@ -15,19 +15,13 @@ use Symfony\Bundle\MakerBundle\Test\MakerTestCase; use Symfony\Bundle\MakerBundle\Test\MakerTestDetails; +/** + * @group legacy + */ class MakeFunctionalTestTest extends MakerTestCase { public function getTestDetails() { - yield 'functional_maker' => [MakerTestDetails::createTest( - $this->getMakerInstance(MakeFunctionalTest::class), - [ - // functional test class name - 'FooBar', - ]) - ->setFixtureFilesPath(__DIR__.'/../fixtures/MakeFunctional'), - ]; - yield 'functional_with_panther' => [MakerTestDetails::createTest( $this->getMakerInstance(MakeFunctionalTest::class), [ diff --git a/tests/Maker/MakeTestTest.php b/tests/Maker/MakeTestTest.php new file mode 100644 index 000000000..973b073b3 --- /dev/null +++ b/tests/Maker/MakeTestTest.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MakerBundle\Tests\Maker; + +use Symfony\Bundle\MakerBundle\Maker\MakeTest; +use Symfony\Bundle\MakerBundle\Test\MakerTestCase; +use Symfony\Bundle\MakerBundle\Test\MakerTestDetails; + +class MakeTestTest extends MakerTestCase +{ + public function getTestDetails() + { + yield 'TestCase' => [MakerTestDetails::createTest( + $this->getMakerInstance(MakeTest::class), + [ + // class name + 'FooBar', + // type + 'TestCase', + ]), + ]; + + yield 'KernelTestCase' => [MakerTestDetails::createTest( + $this->getMakerInstance(MakeTest::class), + [ + // functional test class name + 'FooBar', + // type + 'KernelTestCase', + ]) + ->setFixtureFilesPath(__DIR__.'/../fixtures/MakeFunctional'), + ]; + + yield 'WebTestCase' => [MakerTestDetails::createTest( + $this->getMakerInstance(MakeTest::class), + [ + // functional test class name + 'FooBar', + // type + 'WebTestCase', + ]) + ->setFixtureFilesPath(__DIR__.'/../fixtures/MakeFunctional'), + ]; + + yield 'PantherTestCase' => [MakerTestDetails::createTest( + $this->getMakerInstance(MakeTest::class), + [ + // functional test class name + 'FooBar', + // type + 'PantherTestCase', + ]) + ->addExtraDependencies('panther') + ->setFixtureFilesPath(__DIR__.'/../fixtures/MakeFunctional'), + ]; + } +} diff --git a/tests/Maker/MakeUnitTestTest.php b/tests/Maker/MakeUnitTestTest.php index 64b6c9762..d64fc3822 100644 --- a/tests/Maker/MakeUnitTestTest.php +++ b/tests/Maker/MakeUnitTestTest.php @@ -15,6 +15,9 @@ use Symfony\Bundle\MakerBundle\Test\MakerTestCase; use Symfony\Bundle\MakerBundle\Test\MakerTestDetails; +/** + * @group legacy + */ class MakeUnitTestTest extends MakerTestCase { public function getTestDetails()