Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[make:*] add ability to generate tests #1497

Merged
merged 3 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading