Skip to content

Commit

Permalink
feature #1497 [make:*] add ability to generate tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jrushlow committed Apr 21, 2024
1 parent 48d317e commit e7ea13d
Show file tree
Hide file tree
Showing 12 changed files with 531 additions and 2 deletions.
60 changes: 60 additions & 0 deletions src/Maker/Common/CanGenerateTestsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

/*
* This file is part of the Symfony MakerBundle package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bundle\MakerBundle\Maker\Common;

use Symfony\Bundle\MakerBundle\ConsoleStyle;
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;

/**
* @author Jesse Rushlow <jr@rushlow.dev>
*
* @internal
*/
trait CanGenerateTestsTrait
{
private bool $generateTests = false;

public function configureCommandWithTestsOption(Command $command): Command
{
$testsHelp = file_get_contents(\dirname(__DIR__, 2).'/Resources/help/_WithTests.txt');
$help = $command->getHelp()."\n".$testsHelp;

$command
->addOption(name: 'with-tests', mode: InputOption::VALUE_NONE, description: 'Generate PHPUnit Tests')
->setHelp($help)
;

return $command;
}

public function interactSetGenerateTests(InputInterface $input, ConsoleStyle $io): void
{
// Sanity check for maker dev's - End user should never see this.
if (!$input->hasOption('with-tests')) {
throw new RuntimeCommandException('Whoops! "--with-tests" option does not exist. Call "addWithTestsOptions()" in the makers "configureCommand().');
}

$this->generateTests = $input->getOption('with-tests');

if (!$this->generateTests) {
$this->generateTests = $io->confirm('Do you want to generate PHPUnit tests? [Experimental]', false);
}
}

public function shouldGenerateTests(): bool
{
return $this->generateTests;
}
}
8 changes: 6 additions & 2 deletions src/Maker/MakeCrud.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\InputConfiguration;
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;
Expand All @@ -45,6 +46,8 @@
*/
final class MakeCrud extends AbstractMaker
{
use CanGenerateTestsTrait;

private Inflector $inflector;
private string $controllerClassName;
private bool $generateTests = false;
Expand Down Expand Up @@ -72,6 +75,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
;

$inputConfig->setArgumentAsNonInteractive('entity-class');
$this->configureCommandWithTestsOption($command);
}

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

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

public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
Expand Down Expand Up @@ -237,7 +241,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
);
}

if ($this->generateTests) {
if ($this->shouldGenerateTests()) {
$testClassDetails = $generator->createClassNameDetails(
$entityClassDetails->getRelativeNameWithoutSuffix(),
'Test\\Controller\\',
Expand Down
39 changes: 39 additions & 0 deletions src/Maker/MakeRegistrationForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,22 @@
namespace Symfony\Bundle\MakerBundle\Maker;

use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\Column;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\MakerBundle\ConsoleStyle;
use Symfony\Bundle\MakerBundle\DependencyBuilder;
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
use Symfony\Bundle\MakerBundle\FileManager;
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\InputConfiguration;
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer;
use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper;
use Symfony\Bundle\MakerBundle\Security\Model\Authenticator;
Expand Down Expand Up @@ -68,6 +72,8 @@
*/
final class MakeRegistrationForm extends AbstractMaker
{
use CanGenerateTestsTrait;

private string $userClass;
private string $usernameField;
private string $passwordField;
Expand Down Expand Up @@ -104,6 +110,8 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
$command
->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeRegistrationForm.txt'))
;

$this->configureCommandWithTestsOption($command);
}

public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
Expand Down Expand Up @@ -180,6 +188,8 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
$routeNames = array_keys($this->router->getRouteCollection()->all());
$this->redirectRouteName = $io->choice('What route should the user be redirected to after registration?', $routeNames);
}

$this->interactSetGenerateTests($input, $io);
}

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

// Generate PHPUnit Tests
if ($this->shouldGenerateTests()) {
$testClassDetails = $generator->createClassNameDetails(
'RegistrationControllerTest',
'Test\\'
);

$useStatements = new UseStatementGenerator([
EntityManager::class,
KernelBrowser::class,
TemplatedEmail::class,
WebTestCase::class,
$userRepoVars['repository_full_class_name'],
]);

$generator->generateFile(
targetPath: sprintf('tests/%s.php', $testClassDetails->getShortName()),
templateName: $this->willVerifyEmail ? 'registration/Test.WithVerify.tpl.php' : 'registration/Test.WithoutVerify.tpl.php',
variables: array_merge([
'use_statements' => $useStatements,
'from_email' => $this->fromEmailAddress ?? null,
], $userRepoVars)
);

if (!class_exists(WebTestCase::class)) {
$io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.');
}
}

$generator->writeChanges();

$this->writeSuccessMessage($io);
Expand Down
53 changes: 53 additions & 0 deletions src/Maker/MakeResetPassword.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use Symfony\Bridge\Twig\AppVariable;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\MakerBundle\ConsoleStyle;
use Symfony\Bundle\MakerBundle\DependencyBuilder;
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
Expand All @@ -26,6 +28,7 @@
use Symfony\Bundle\MakerBundle\FileManager;
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\InputConfiguration;
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
use Symfony\Bundle\MakerBundle\Maker\Common\UidTrait;
use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper;
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
Expand All @@ -51,6 +54,8 @@
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Route as RouteObject;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Translation\Translator;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
Expand Down Expand Up @@ -81,11 +86,13 @@
*/
class MakeResetPassword extends AbstractMaker
{
use CanGenerateTestsTrait;
use UidTrait;

private string $fromEmailAddress;
private string $fromEmailName;
private string $controllerResetSuccessRedirect;
private ?RouteObject $controllerResetSuccessRoute = null;
private string $userClass;
private string $emailPropertyName;
private string $emailGetterMethodName;
Expand All @@ -95,6 +102,7 @@ public function __construct(
private FileManager $fileManager,
private DoctrineHelper $doctrineHelper,
private EntityClassGenerator $entityClassGenerator,
private ?RouterInterface $router = null,
) {
}

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

$this->addWithUuidOption($command);
$this->configureCommandWithTestsOption($command);
}

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

if ($this->router instanceof RouterInterface) {
$this->controllerResetSuccessRoute = $this->router->getRouteCollection()->get($this->controllerResetSuccessRedirect);
}

$io->section('- Email -');
$emailText[] = 'These are used to generate the email code. Don\'t worry, you can change them in the code later!';
$io->text($emailText);
Expand All @@ -187,6 +200,8 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
null,
Validator::notBlank(...)
);

$this->interactSetGenerateTests($input, $io);
}

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

// Generate PHPUnit tests
if ($this->shouldGenerateTests()) {
$testClassDetails = $generator->createClassNameDetails(
'ResetPasswordControllerTest',
'Test\\',
);

$userRepositoryDetails = $generator->createClassNameDetails(
sprintf('%sRepository', $userClassNameDetails->getShortName()),
'Repository\\'
);

$useStatements = new UseStatementGenerator([
$userClassNameDetails->getFullName(),
$userRepositoryDetails->getFullName(),
EntityManagerInterface::class,
KernelBrowser::class,
WebTestCase::class,
UserPasswordHasherInterface::class,
]);

$generator->generateFile(
targetPath: sprintf('tests/%s.php', $testClassDetails->getShortName()),
templateName: 'resetPassword/Test.ResetPasswordController.tpl.php',
variables: [
'use_statements' => $useStatements,
'user_short_name' => $userClassNameDetails->getShortName(),
'user_repo_short_name' => $userRepositoryDetails->getShortName(),
'success_route_path' => null !== $this->controllerResetSuccessRoute ? $this->controllerResetSuccessRoute->getPath() : '/',
'from_email' => $this->fromEmailAddress,
],
);

if (!class_exists(WebTestCase::class)) {
$io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.');
}
}

$generator->writeChanges();

$this->writeSuccessMessage($io);
Expand Down
1 change: 1 addition & 0 deletions src/Resources/config/makers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<argument type="service" id="maker.file_manager" />
<argument type="service" id="maker.doctrine_helper" />
<argument type="service" id="maker.entity_class_generator" />
<argument type="service" id="router" on-invalid="ignore" />
<tag name="maker.command" />
</service>

Expand Down
6 changes: 6 additions & 0 deletions src/Resources/help/_WithTests.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
To generate tailored PHPUnit tests, simply call:

<info>php %command.full_name% --with-tests</info>

This will generate a unit test in <info>tests/</info> for you to review then use
to test the new functionality of your app.
Loading

0 comments on commit e7ea13d

Please sign in to comment.