Skip to content

Commit eedc4ae

Browse files
Pierstovaltacman
andauthored
@tacman followup (#24)
Improve Maker command and support more PHP versions --------- Co-authored-by: Tac Tacelosky <[email protected]>
1 parent ba5eeb6 commit eedc4ae

15 files changed

+467
-40
lines changed

.github/workflows/php.yml

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ jobs:
99
strategy:
1010
matrix:
1111
include:
12-
- { php-version: "8.1", symfony-version: "6.0", phpunit-version: "9.6" }
1312
- { php-version: "8.1", symfony-version: "6.1", phpunit-version: "9.6" }
1413
- { php-version: "8.1", symfony-version: "6.2", phpunit-version: "9.6" }
1514
- { php-version: "8.1", symfony-version: "6.3", phpunit-version: "9.6" }
1615
- { php-version: "8.1", symfony-version: "6.4", phpunit-version: "9.6" }
1716

18-
- { php-version: "8.2", symfony-version: "6.0", phpunit-version: "9.6" }
1917
- { php-version: "8.2", symfony-version: "6.1", phpunit-version: "9.6" }
2018
- { php-version: "8.2", symfony-version: "6.2", phpunit-version: "9.6" }
2119
- { php-version: "8.2", symfony-version: "6.3", phpunit-version: "9.6" }
@@ -24,13 +22,11 @@ jobs:
2422
- { php-version: "8.2", symfony-version: "7.1", phpunit-version: "9.6" }
2523
- { php-version: "8.2", symfony-version: "7.2", phpunit-version: "9.6" }
2624

27-
- { php-version: "8.1", symfony-version: "6.0", phpunit-version: "10" }
2825
- { php-version: "8.1", symfony-version: "6.1", phpunit-version: "10" }
2926
- { php-version: "8.1", symfony-version: "6.2", phpunit-version: "10" }
3027
- { php-version: "8.1", symfony-version: "6.3", phpunit-version: "10" }
3128
- { php-version: "8.1", symfony-version: "6.4", phpunit-version: "10" }
3229

33-
- { php-version: "8.2", symfony-version: "6.0", phpunit-version: "10" }
3430
- { php-version: "8.2", symfony-version: "6.1", phpunit-version: "10" }
3531
- { php-version: "8.2", symfony-version: "6.2", phpunit-version: "10" }
3632
- { php-version: "8.2", symfony-version: "6.3", phpunit-version: "10" }
@@ -39,6 +35,30 @@ jobs:
3935
- { php-version: "8.2", symfony-version: "7.1", phpunit-version: "10" }
4036
- { php-version: "8.2", symfony-version: "7.2", phpunit-version: "10" }
4137

38+
- { php-version: "8.3", symfony-version: "6.1", phpunit-version: "9.6" }
39+
- { php-version: "8.3", symfony-version: "6.2", phpunit-version: "9.6" }
40+
- { php-version: "8.3", symfony-version: "6.3", phpunit-version: "9.6" }
41+
- { php-version: "8.3", symfony-version: "6.4", phpunit-version: "9.6" }
42+
- { php-version: "8.3", symfony-version: "7.0", phpunit-version: "9.6" }
43+
- { php-version: "8.3", symfony-version: "7.1", phpunit-version: "9.6" }
44+
- { php-version: "8.3", symfony-version: "7.2", phpunit-version: "9.6" }
45+
46+
- { php-version: "8.4", symfony-version: "7.0", phpunit-version: "9.6" }
47+
- { php-version: "8.4", symfony-version: "7.1", phpunit-version: "9.6" }
48+
- { php-version: "8.4", symfony-version: "7.2", phpunit-version: "9.6" }
49+
50+
- { php-version: "8.3", symfony-version: "6.1", phpunit-version: "10" }
51+
- { php-version: "8.3", symfony-version: "6.2", phpunit-version: "10" }
52+
- { php-version: "8.3", symfony-version: "6.3", phpunit-version: "10" }
53+
- { php-version: "8.3", symfony-version: "6.4", phpunit-version: "10" }
54+
- { php-version: "8.3", symfony-version: "7.0", phpunit-version: "10" }
55+
- { php-version: "8.3", symfony-version: "7.1", phpunit-version: "10" }
56+
- { php-version: "8.3", symfony-version: "7.2", phpunit-version: "10" }
57+
58+
- { php-version: "8.4", symfony-version: "7.0", phpunit-version: "10" }
59+
- { php-version: "8.4", symfony-version: "7.1", phpunit-version: "10" }
60+
- { php-version: "8.4", symfony-version: "7.2", phpunit-version: "10" }
61+
4262
name: PHP ${{ matrix.php-version }}, Symfony ${{ matrix.symfony-version }}, PHPUnit ${{ matrix.phpunit-version }}
4363
steps:
4464
- uses: actions/checkout@v4
@@ -61,7 +81,7 @@ jobs:
6181

6282
- name: Install dependencies
6383
run: |
64-
composer update --with "symfony/framework-bundle:^${{ matrix.symfony-version }}" --with "phpunit/phpunit:^${{ matrix.phpunit-version }}"
84+
composer update --with "symfony/framework-bundle:${{ matrix.symfony-version }}.*" --with "phpunit/phpunit:${{ matrix.phpunit-version }}.*"
6585
6686
- name: Setup fixture app
6787
working-directory: ./fixture-app

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## v1.2.0
2+
3+
* Make sure the library is usable for PHP 8.3 and 8.4
4+
* Add `--use-attributes` option to the `make:smoke-tests` command: this will generate smoke tests using PHPUnit's `#[TestWith]` attribute
5+
* Add `--use-provider` option to the `make:smoke-tests` command: this will generate smoke tests using PHPUnit's `#[DataProvider]` attribute<br>(note: both aforementioned attributes cannot be used at the same time)
6+
17
## v1.1.1
28

39
* Fixes how routes with dynamic host and schemes were taken in account while they shouldn't have.

fixture-app/bin/console

100644100755
File mode changed.

fixture-app/bin/phpunit

100644100755
File mode changed.

src/Maker/MakeSmokeTests.php

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,48 @@ public static function getCommandDescription(): string
3535

3636
public function configureCommand(Command $command, InputConfiguration $inputConfig)
3737
{
38-
$command->addOption('dto', null, InputOption::VALUE_NEGATABLE, \sprintf('Enables (or disable --no-dto) tests using the "%s" DTO class.', \basename(FunctionalTestData::class)), true);
38+
$command->addOption('dto', 'd', InputOption::VALUE_NEGATABLE, \sprintf('Enables (or disable --no-dto) tests using the "%s" DTO class.', \basename(\str_replace('\\', '/', FunctionalTestData::class))), true);
39+
$command->addOption('use-attributes', 'a', InputOption::VALUE_NONE, 'If enabled, uses in #[TestWith] attribute to provide routes.');
40+
$command->addOption('use-provider', 'p', InputOption::VALUE_NONE, 'If enabled, uses in #[DataProvider] attribute to provide routes.');
3941
}
4042

41-
public function configureDependencies(DependencyBuilder $dependencies)
43+
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
44+
{
45+
parent::interact($input, $io, $command);
46+
47+
if ($input->getOption('use-attributes') && $input->getOption('use-provider')) {
48+
throw new \RuntimeException(\sprintf(
49+
'Cannot set both "%s" and "%s" attribute at the same time. Use only one of them.',
50+
'use-attributes', 'use-provider'
51+
));
52+
}
53+
}
54+
55+
public function configureDependencies(DependencyBuilder $dependencies): void
4256
{
4357
}
4458

45-
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
59+
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
4660
{
4761
if (!$this->router->getRouteCollection()->count()) {
4862
throw new \RuntimeException('No routes found in the application.');
4963
}
5064

5165
$stateProviderClassNameDetails = $generator->createClassNameDetails('Functional', 'Tests', 'Test');
5266

53-
$generator->generateClass($stateProviderClassNameDetails->getFullName(), __DIR__.'/Resources/FunctionalSmokeTest.tpl.php', [
67+
if ($input->getOption('use-attributes')) {
68+
$template = __DIR__ . '/Resources/FunctionalSmokeTest.attributes.tpl.php';
69+
} elseif ($input->getOption('use-provider')) {
70+
$template = __DIR__ . '/Resources/FunctionalSmokeTest.provider.tpl.php';
71+
} else {
72+
$template = __DIR__ . '/Resources/FunctionalSmokeTest.tpl.php';
73+
}
74+
75+
$generator->generateClass($stateProviderClassNameDetails->getFullName(), $template, [
5476
'routes' => RoutesExtractor::extractRoutesFromRouter($this->router),
5577
'with_dto' => $input->getOption('dto'),
5678
]);
79+
5780
$generator->writeChanges();
5881

5982
$this->writeSuccessMessage($io);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php declare(strict_types=1);
2+
echo "<?php\n"; ?>
3+
4+
namespace <?php echo $namespace; ?>;
5+
6+
use PHPUnit\Framework\Attributes\TestDox;
7+
use PHPUnit\Framework\Attributes\TestWith;
8+
<?php if ($with_dto): ?>
9+
use Pierstoval\SmokeTesting\FunctionalSmokeTester;
10+
use Pierstoval\SmokeTesting\FunctionalTestData;
11+
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
12+
<?php endif; ?>
13+
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
14+
15+
class <?php echo $class_name; ?> extends WebTestCase
16+
{
17+
<?php if ($with_dto): ?>
18+
use FunctionalSmokeTester;
19+
<?php endif; ?>
20+
21+
<?php foreach ($routes as $route){
22+
if (str_starts_with($route['routePath'], '/_')) { continue; } ?>
23+
#[TestWith(['<?php echo $route['httpMethod']; ?>', '<?php echo $route['routePath']; ?>', '<?php echo $route['routeName']; ?>'])]
24+
<?php } ?>
25+
#[TestDox('$method $url ($route)')]
26+
public function testRoute(string $method, string $url, string $route): void
27+
{
28+
<?php if ($with_dto): ?>
29+
$this->runFunctionalTest(
30+
FunctionalTestData::withUrl($url)
31+
->withMethod($method)
32+
->expectRouteName($route)
33+
->appendCallableExpectation($this->assertStatusCodeLessThan500($method, $url))
34+
);
35+
<?php else: ?>
36+
$client = static::createClient();
37+
$client->request('GET', '/');
38+
39+
static::assertLessThan(
40+
500,
41+
$client->getResponse()->getStatusCode(),
42+
\sprintf(
43+
'Request "%s %s" for route "%s" returned an internal error.',
44+
$method, $url, $route
45+
),
46+
);
47+
<?php endif; ?>
48+
}
49+
50+
<?php if ($with_dto): ?>
51+
public function assertStatusCodeLessThan500(string $method, string $url): \Closure
52+
{
53+
return static function (KernelBrowser $browser) use ($method, $url) {
54+
$statusCode = $browser->getResponse()->getStatusCode();
55+
$routeName = $browser->getRequest()->attributes->get('_route', 'unknown');
56+
57+
static::assertLessThan(
58+
500,
59+
$statusCode,
60+
sprintf('Request "%s %s" for %s route returned an internal error.', $method, $url, $routeName),
61+
);
62+
};
63+
}
64+
<?php endif; ?>}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php declare(strict_types=1);
2+
echo "<?php\n"; ?>
3+
4+
namespace <?php echo $namespace; ?>;
5+
6+
use PHPUnit\Framework\Attributes\DataProvider;
7+
<?php if ($with_dto): ?>
8+
use Pierstoval\SmokeTesting\FunctionalSmokeTester;
9+
use Pierstoval\SmokeTesting\FunctionalTestData;
10+
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
11+
<?php endif; ?>
12+
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
13+
14+
class <?php echo $class_name; ?> extends WebTestCase
15+
{
16+
<?php if ($with_dto): ?>
17+
use FunctionalSmokeTester;
18+
<?php endif; ?>
19+
20+
public static function provideRoutes(): \Generator
21+
{
22+
<?php foreach ($routes as $route): ?>
23+
<?php if (str_starts_with($route['routePath'], '/_')) continue;
24+
?> yield '<?php echo $route['httpMethod']; ?> <?php echo $route['routePath']; ?>' => ['<?php echo $route['httpMethod']; ?>', '<?php echo $route['routePath']; ?>','<?php echo $route['routeName']; ?>'];
25+
<?php endforeach; ?>
26+
}
27+
28+
#[DataProvider('provideRoutes')]
29+
public function testRoute(string $method, string $url, string $route): void
30+
{
31+
<?php if ($with_dto): ?>
32+
$this->runFunctionalTest(
33+
FunctionalTestData::withUrl($url)
34+
->withMethod($method)
35+
->expectRouteName($route)
36+
->appendCallableExpectation($this->assertStatusCodeLessThan500($method, $url))
37+
);
38+
<?php else: ?>
39+
$client = static::createClient();
40+
$client->request($method, $url);
41+
42+
static::assertLessThan(
43+
500,
44+
$client->getResponse()->getStatusCode(),
45+
\sprintf(
46+
'Request "%s %s" for route "%s" returned an internal error.',
47+
$method, $url, $route
48+
),
49+
);
50+
<?php endif; ?>
51+
52+
}
53+
54+
<?php if ($with_dto): ?>
55+
public function assertStatusCodeLessThan500(string $method, string $url): \Closure
56+
{
57+
return static function (KernelBrowser $browser) use ($method, $url) {
58+
$statusCode = $browser->getResponse()->getStatusCode();
59+
$routeName = $browser->getRequest()->attributes->get('_route', 'unknown');
60+
61+
static::assertLessThan(
62+
500,
63+
$statusCode,
64+
sprintf('Request "%s %s" for %s route returned an internal error.', $method, $url, $routeName),
65+
);
66+
};
67+
}
68+
<?php endif; ?>}

src/Maker/Resources/FunctionalSmokeTest.tpl.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function testRoute<?php echo ucfirst(preg_replace_callback('~_([a-z0-9])~
2828
);
2929
<?php else: ?>
3030
$client = static::createClient();
31-
$crawler = $client->request('GET', '/');
31+
$client->request('GET', '/');
3232

3333
static::assertLessThan(
3434
500,
@@ -42,7 +42,7 @@ public function testRoute<?php echo ucfirst(preg_replace_callback('~_([a-z0-9])~
4242
<?php if ($with_dto): ?>
4343
public function assertStatusCodeLessThan500(string $method, string $url): \Closure
4444
{
45-
return function (KernelBrowser $browser) use ($method, $url) {
45+
return static function (KernelBrowser $browser) use ($method, $url) {
4646
$statusCode = $browser->getResponse()->getStatusCode();
4747
$routeName = $browser->getRequest()->attributes->get('_route', 'unknown');
4848

tests/MakeSmokeTestsTest.php

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,31 @@ protected function setup(): void
1717
/**
1818
* @dataProvider provideSmokeTestCases
1919
*/
20-
public function testMakeStateProvider(bool $useDto): void
20+
public function testMakeStateProvider(bool $useDto, bool $useAttributes = false, bool $useProvider = false): void
2121
{
2222
$tester = new CommandTester((new Application(self::bootKernel()))->find('make:smoke-tests'));
23-
$tester->execute([$useDto ? '--dto' : '--no-dto' => $useDto]);
23+
$params = [
24+
$useDto ? '--dto' : '--no-dto' => $useDto,
25+
];
26+
if ($useAttributes) {
27+
$params['--use-attributes'] = true;
28+
}
29+
if ($useProvider) {
30+
$params['--use-provider'] = true;
31+
}
32+
$tester->execute($params);
2433

2534
$newTestFile = self::generatedTestFile();
2635
$this->assertFileExists($newTestFile);
2736

28-
$comparedFile = $useDto
29-
? __DIR__ . '/fixtures/FunctionalTestWithDto.php'
30-
: __DIR__ . '/fixtures/FunctionalTestWithoutDto.php'
31-
;
37+
$comparedFile = match (true) {
38+
$useDto && $useAttributes => __DIR__ . '/fixtures/FunctionalTestWithDto.attributes.php',
39+
$useDto && $useProvider => __DIR__ . '/fixtures/FunctionalTestWithDto.provider.php',
40+
!$useDto && $useAttributes => __DIR__ . '/fixtures/FunctionalTestWithoutDto.attributes.php',
41+
!$useDto && $useProvider => __DIR__ . '/fixtures/FunctionalTestWithoutDto.provider.php',
42+
$useDto && !$useAttributes && !$useProvider => __DIR__ . '/fixtures/FunctionalTestWithDto.php',
43+
!$useDto && !$useAttributes && !$useProvider => __DIR__ . '/fixtures/FunctionalTestWithoutDto.php',
44+
};
3245

3346
// Unify line endings
3447
$expected = preg_replace("~\n +\n~", "\n\n", preg_replace('~\R~u', "\n", file_get_contents($comparedFile)));
@@ -42,13 +55,12 @@ public function testMakeStateProvider(bool $useDto): void
4255

4356
public static function provideSmokeTestCases(): \Generator
4457
{
45-
yield 'Generate smoke tests with DTO' => [
46-
'useDto' => true,
47-
];
48-
49-
yield 'Generate smoke tests without DTO' => [
50-
'useDto' => false,
51-
];
58+
yield 'Generate smoke tests with DTO' => ['useDto' => true];
59+
yield 'Generate smoke tests without DTO' => ['useDto' => false];
60+
yield 'Generate smoke tests with DTO and TestWith attributes' => ['useDto' => true, 'useAttributes' => true];
61+
yield 'Generate smoke tests without DTO and TestWith attributes' => ['useDto' => false, 'useAttributes' => true];
62+
yield 'Generate smoke tests with DTO and DatProvider' => ['useDto' => true, 'useProvider' => true];
63+
yield 'Generate smoke tests without DTO and DatProvider' => ['useDto' => false, 'useProvider' => true];
5264
}
5365

5466
private static function generatedTestFile(): string
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace App\Tests;
4+
5+
use PHPUnit\Framework\Attributes\TestDox;
6+
use PHPUnit\Framework\Attributes\TestWith;
7+
use Pierstoval\SmokeTesting\FunctionalSmokeTester;
8+
use Pierstoval\SmokeTesting\FunctionalTestData;
9+
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
10+
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
11+
12+
class FunctionalTest extends WebTestCase
13+
{
14+
use FunctionalSmokeTester;
15+
16+
#[TestWith(['GET', '/other_param', 'get_param_with_default'])]
17+
#[TestWith(['GET', '/200', 'get_200'])]
18+
#[TestWith(['GET', '/302', 'get_302'])]
19+
#[TestWith(['GET', '/400', 'get_400'])]
20+
#[TestWith(['GET', '/500', 'get_500'])]
21+
#[TestWith(['GET', '/payload', 'get_with_payload'])]
22+
#[TestWith(['GET', '/json/valid', 'json_valid'])]
23+
#[TestWith(['GET', '/json/valid-header', 'json_valid_header'])]
24+
#[TestWith(['GET', '/json/missing_header', 'json_missing_header'])]
25+
#[TestWith(['GET', '/json/invalid', 'json_invalid'])]
26+
#[TestWith(['GET', '/cookie/value', 'cookie_value'])]
27+
#[TestWith(['GET', 'http://test.localhost/host/fixed', 'host_fixed'])]
28+
#[TestWith(['GET', 'http://localhost/scheme/fixed', 'scheme_fixed'])]
29+
#[TestWith(['GET', '/content-type', 'content_type'])]
30+
#[TestWith(['POST', '/post', 'post_route'])]
31+
#[TestDox('$method $url ($route)')]
32+
public function testRoute(string $method, string $url, string $route): void
33+
{
34+
$this->runFunctionalTest(
35+
FunctionalTestData::withUrl($url)
36+
->withMethod($method)
37+
->expectRouteName($route)
38+
->appendCallableExpectation($this->assertStatusCodeLessThan500($method, $url))
39+
);
40+
}
41+
42+
public function assertStatusCodeLessThan500(string $method, string $url): \Closure
43+
{
44+
return static function (KernelBrowser $browser) use ($method, $url) {
45+
$statusCode = $browser->getResponse()->getStatusCode();
46+
$routeName = $browser->getRequest()->attributes->get('_route', 'unknown');
47+
48+
static::assertLessThan(
49+
500,
50+
$statusCode,
51+
sprintf('Request "%s %s" for %s route returned an internal error.', $method, $url, $routeName),
52+
);
53+
};
54+
}
55+
}

0 commit comments

Comments
 (0)