Skip to content

Commit e7ea13d

Browse files
authored
feature #1497 [make:*] add ability to generate tests
1 parent 48d317e commit e7ea13d

File tree

12 files changed

+531
-2
lines changed

12 files changed

+531
-2
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle\Maker\Common;
13+
14+
use Symfony\Bundle\MakerBundle\ConsoleStyle;
15+
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
16+
use Symfony\Component\Console\Command\Command;
17+
use Symfony\Component\Console\Input\InputInterface;
18+
use Symfony\Component\Console\Input\InputOption;
19+
20+
/**
21+
* @author Jesse Rushlow <[email protected]>
22+
*
23+
* @internal
24+
*/
25+
trait CanGenerateTestsTrait
26+
{
27+
private bool $generateTests = false;
28+
29+
public function configureCommandWithTestsOption(Command $command): Command
30+
{
31+
$testsHelp = file_get_contents(\dirname(__DIR__, 2).'/Resources/help/_WithTests.txt');
32+
$help = $command->getHelp()."\n".$testsHelp;
33+
34+
$command
35+
->addOption(name: 'with-tests', mode: InputOption::VALUE_NONE, description: 'Generate PHPUnit Tests')
36+
->setHelp($help)
37+
;
38+
39+
return $command;
40+
}
41+
42+
public function interactSetGenerateTests(InputInterface $input, ConsoleStyle $io): void
43+
{
44+
// Sanity check for maker dev's - End user should never see this.
45+
if (!$input->hasOption('with-tests')) {
46+
throw new RuntimeCommandException('Whoops! "--with-tests" option does not exist. Call "addWithTestsOptions()" in the makers "configureCommand().');
47+
}
48+
49+
$this->generateTests = $input->getOption('with-tests');
50+
51+
if (!$this->generateTests) {
52+
$this->generateTests = $io->confirm('Do you want to generate PHPUnit tests? [Experimental]', false);
53+
}
54+
}
55+
56+
public function shouldGenerateTests(): bool
57+
{
58+
return $this->generateTests;
59+
}
60+
}

src/Maker/MakeCrud.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
2525
use Symfony\Bundle\MakerBundle\Generator;
2626
use Symfony\Bundle\MakerBundle\InputConfiguration;
27+
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
2728
use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer;
2829
use Symfony\Bundle\MakerBundle\Str;
2930
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
@@ -45,6 +46,8 @@
4546
*/
4647
final class MakeCrud extends AbstractMaker
4748
{
49+
use CanGenerateTestsTrait;
50+
4851
private Inflector $inflector;
4952
private string $controllerClassName;
5053
private bool $generateTests = false;
@@ -72,6 +75,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
7275
;
7376

7477
$inputConfig->setArgumentAsNonInteractive('entity-class');
78+
$this->configureCommandWithTestsOption($command);
7579
}
7680

7781
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
@@ -96,7 +100,7 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
96100
$defaultControllerClass
97101
);
98102

99-
$this->generateTests = $io->confirm('Do you want to generate tests for the controller? [Experimental]', false);
103+
$this->interactSetGenerateTests($input, $io);
100104
}
101105

102106
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
@@ -237,7 +241,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
237241
);
238242
}
239243

240-
if ($this->generateTests) {
244+
if ($this->shouldGenerateTests()) {
241245
$testClassDetails = $generator->createClassNameDetails(
242246
$entityClassDetails->getRelativeNameWithoutSuffix(),
243247
'Test\\Controller\\',

src/Maker/MakeRegistrationForm.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,22 @@
1212
namespace Symfony\Bundle\MakerBundle\Maker;
1313

1414
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
15+
use Doctrine\ORM\EntityManager;
1516
use Doctrine\ORM\EntityManagerInterface;
1617
use Doctrine\ORM\Mapping\Column;
1718
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
1819
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
1920
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
21+
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
22+
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
2023
use Symfony\Bundle\MakerBundle\ConsoleStyle;
2124
use Symfony\Bundle\MakerBundle\DependencyBuilder;
2225
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
2326
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
2427
use Symfony\Bundle\MakerBundle\FileManager;
2528
use Symfony\Bundle\MakerBundle\Generator;
2629
use Symfony\Bundle\MakerBundle\InputConfiguration;
30+
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
2731
use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer;
2832
use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper;
2933
use Symfony\Bundle\MakerBundle\Security\Model\Authenticator;
@@ -68,6 +72,8 @@
6872
*/
6973
final class MakeRegistrationForm extends AbstractMaker
7074
{
75+
use CanGenerateTestsTrait;
76+
7177
private string $userClass;
7278
private string $usernameField;
7379
private string $passwordField;
@@ -104,6 +110,8 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
104110
$command
105111
->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeRegistrationForm.txt'))
106112
;
113+
114+
$this->configureCommandWithTestsOption($command);
107115
}
108116

109117
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
@@ -180,6 +188,8 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
180188
$routeNames = array_keys($this->router->getRouteCollection()->all());
181189
$this->redirectRouteName = $io->choice('What route should the user be redirected to after registration?', $routeNames);
182190
}
191+
192+
$this->interactSetGenerateTests($input, $io);
183193
}
184194

185195
/** @param array<string, mixed> $securityData */
@@ -403,6 +413,35 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
403413
$this->fileManager->dumpFile($classDetails->getPath(), $userManipulator->getSourceCode());
404414
}
405415

416+
// Generate PHPUnit Tests
417+
if ($this->shouldGenerateTests()) {
418+
$testClassDetails = $generator->createClassNameDetails(
419+
'RegistrationControllerTest',
420+
'Test\\'
421+
);
422+
423+
$useStatements = new UseStatementGenerator([
424+
EntityManager::class,
425+
KernelBrowser::class,
426+
TemplatedEmail::class,
427+
WebTestCase::class,
428+
$userRepoVars['repository_full_class_name'],
429+
]);
430+
431+
$generator->generateFile(
432+
targetPath: sprintf('tests/%s.php', $testClassDetails->getShortName()),
433+
templateName: $this->willVerifyEmail ? 'registration/Test.WithVerify.tpl.php' : 'registration/Test.WithoutVerify.tpl.php',
434+
variables: array_merge([
435+
'use_statements' => $useStatements,
436+
'from_email' => $this->fromEmailAddress ?? null,
437+
], $userRepoVars)
438+
);
439+
440+
if (!class_exists(WebTestCase::class)) {
441+
$io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.');
442+
}
443+
}
444+
406445
$generator->writeChanges();
407446

408447
$this->writeSuccessMessage($io);

src/Maker/MakeResetPassword.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use Symfony\Bridge\Twig\AppVariable;
1717
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
1818
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
19+
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
20+
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
1921
use Symfony\Bundle\MakerBundle\ConsoleStyle;
2022
use Symfony\Bundle\MakerBundle\DependencyBuilder;
2123
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
@@ -26,6 +28,7 @@
2628
use Symfony\Bundle\MakerBundle\FileManager;
2729
use Symfony\Bundle\MakerBundle\Generator;
2830
use Symfony\Bundle\MakerBundle\InputConfiguration;
31+
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
2932
use Symfony\Bundle\MakerBundle\Maker\Common\UidTrait;
3033
use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper;
3134
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
@@ -51,6 +54,8 @@
5154
use Symfony\Component\OptionsResolver\OptionsResolver;
5255
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
5356
use Symfony\Component\Routing\Attribute\Route;
57+
use Symfony\Component\Routing\Route as RouteObject;
58+
use Symfony\Component\Routing\RouterInterface;
5459
use Symfony\Component\Translation\Translator;
5560
use Symfony\Component\Validator\Constraints\Length;
5661
use Symfony\Component\Validator\Constraints\NotBlank;
@@ -81,11 +86,13 @@
8186
*/
8287
class MakeResetPassword extends AbstractMaker
8388
{
89+
use CanGenerateTestsTrait;
8490
use UidTrait;
8591

8692
private string $fromEmailAddress;
8793
private string $fromEmailName;
8894
private string $controllerResetSuccessRedirect;
95+
private ?RouteObject $controllerResetSuccessRoute = null;
8996
private string $userClass;
9097
private string $emailPropertyName;
9198
private string $emailGetterMethodName;
@@ -95,6 +102,7 @@ public function __construct(
95102
private FileManager $fileManager,
96103
private DoctrineHelper $doctrineHelper,
97104
private EntityClassGenerator $entityClassGenerator,
105+
private ?RouterInterface $router = null,
98106
) {
99107
}
100108

@@ -115,6 +123,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
115123
;
116124

117125
$this->addWithUuidOption($command);
126+
$this->configureCommandWithTestsOption($command);
118127
}
119128

120129
public function configureDependencies(DependencyBuilder $dependencies): void
@@ -172,6 +181,10 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
172181
Validator::notBlank(...)
173182
);
174183

184+
if ($this->router instanceof RouterInterface) {
185+
$this->controllerResetSuccessRoute = $this->router->getRouteCollection()->get($this->controllerResetSuccessRedirect);
186+
}
187+
175188
$io->section('- Email -');
176189
$emailText[] = 'These are used to generate the email code. Don\'t worry, you can change them in the code later!';
177190
$io->text($emailText);
@@ -187,6 +200,8 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
187200
null,
188201
Validator::notBlank(...)
189202
);
203+
204+
$this->interactSetGenerateTests($input, $io);
190205
}
191206

192207
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
@@ -334,6 +349,44 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
334349
'resetPassword/twig_reset.tpl.php'
335350
);
336351

352+
// Generate PHPUnit tests
353+
if ($this->shouldGenerateTests()) {
354+
$testClassDetails = $generator->createClassNameDetails(
355+
'ResetPasswordControllerTest',
356+
'Test\\',
357+
);
358+
359+
$userRepositoryDetails = $generator->createClassNameDetails(
360+
sprintf('%sRepository', $userClassNameDetails->getShortName()),
361+
'Repository\\'
362+
);
363+
364+
$useStatements = new UseStatementGenerator([
365+
$userClassNameDetails->getFullName(),
366+
$userRepositoryDetails->getFullName(),
367+
EntityManagerInterface::class,
368+
KernelBrowser::class,
369+
WebTestCase::class,
370+
UserPasswordHasherInterface::class,
371+
]);
372+
373+
$generator->generateFile(
374+
targetPath: sprintf('tests/%s.php', $testClassDetails->getShortName()),
375+
templateName: 'resetPassword/Test.ResetPasswordController.tpl.php',
376+
variables: [
377+
'use_statements' => $useStatements,
378+
'user_short_name' => $userClassNameDetails->getShortName(),
379+
'user_repo_short_name' => $userRepositoryDetails->getShortName(),
380+
'success_route_path' => null !== $this->controllerResetSuccessRoute ? $this->controllerResetSuccessRoute->getPath() : '/',
381+
'from_email' => $this->fromEmailAddress,
382+
],
383+
);
384+
385+
if (!class_exists(WebTestCase::class)) {
386+
$io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.');
387+
}
388+
}
389+
337390
$generator->writeChanges();
338391

339392
$this->writeSuccessMessage($io);

src/Resources/config/makers.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
<argument type="service" id="maker.file_manager" />
9090
<argument type="service" id="maker.doctrine_helper" />
9191
<argument type="service" id="maker.entity_class_generator" />
92+
<argument type="service" id="router" on-invalid="ignore" />
9293
<tag name="maker.command" />
9394
</service>
9495

src/Resources/help/_WithTests.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
To generate tailored PHPUnit tests, simply call:
2+
3+
<info>php %command.full_name% --with-tests</info>
4+
5+
This will generate a unit test in <info>tests/</info> for you to review then use
6+
to test the new functionality of your app.

0 commit comments

Comments
 (0)