Skip to content

Commit 02f172e

Browse files
committed
feat: allow to use Factory::create() in data provider
1 parent 2730f09 commit 02f172e

34 files changed

+877
-183
lines changed

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ DATABASE_URL="mysql://root:[email protected]:3307/foundry_test?serverVersion=5.7.42
22
MONGO_URL="mongodb://127.0.0.1:27018/dbName?compressors=disabled&gssapiServiceName=mongodb"
33
DATABASE_RESET_MODE="schema"
44
USE_DAMA_DOCTRINE_TEST_BUNDLE="0"
5+
USE_FOUNDRY_PHPUNIT_EXTENSION="0"
56
PHPUNIT_VERSION="9"

.github/workflows/ci.yml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88

99
jobs:
1010
tests:
11-
name: P:${{ matrix.php }}, S:${{ matrix.symfony }}, D:${{ matrix.database }}, PU:${{ matrix.phpunit }}${{ matrix.deps == 'lowest' && ' (lowest)' || '' }}${{ matrix.use-dama == 1 && contains(matrix.database, 'sql') && ' (dama)' || '' }}${{ !contains(matrix.database, 'sql') && '' || matrix.use-migrate == 1 && ' (migrate)' || ' (schema)' }}
11+
name: P:${{ matrix.php }}, S:${{ matrix.symfony }}, D:${{ matrix.database }}, PU:${{ matrix.phpunit }}${{ matrix.deps == 'lowest' && ' (lowest)' || '' }}${{ matrix.use-dama == 1 && contains(matrix.database, 'sql') && ' (dama)' || '' }}${{ !contains(matrix.database, 'sql') && '' || matrix.use-migrate == 1 && ' (migrate)' || ' (schema)' }}${{ matrix.use-phpunit-extension == 1 && ' (phpunit extension)' || '' }}
1212
runs-on: ubuntu-latest
1313
strategy:
1414
fail-fast: false
@@ -19,6 +19,7 @@ jobs:
1919
database: [ mysql, mongo ]
2020
use-dama: [ 1 ]
2121
use-migrate: [ 0 ]
22+
use-phpunit-extension: [ 0 ]
2223
phpunit: [ 9 ]
2324
exclude:
2425
- php: 8.1
@@ -32,74 +33,93 @@ jobs:
3233
database: none
3334
use-dama: 1
3435
use-migrate: 0
36+
use-phpunit-extension: 0
3537
phpunit: 9
3638
- php: 8.3
3739
deps: highest
3840
symfony: '*'
3941
database: mysql|mongo
4042
use-dama: 1
4143
use-migrate: 0
44+
use-phpunit-extension: 0
4245
phpunit: 9
4346
- php: 8.3
4447
deps: highest
4548
symfony: '*'
4649
database: pgsql|mongo
4750
use-dama: 1
4851
use-migrate: 0
52+
use-phpunit-extension: 0
4953
phpunit: 9
5054
- php: 8.3
5155
deps: highest
5256
symfony: '*'
5357
database: pgsql
5458
use-dama: 0
5559
use-migrate: 0
60+
use-phpunit-extension: 0
5661
phpunit: 9
5762
- php: 8.3
5863
deps: highest
5964
symfony: '*'
6065
database: sqlite
6166
use-dama: 0
6267
use-migrate: 0
68+
use-phpunit-extension: 0
6369
phpunit: 9
6470
- php: 8.3
6571
deps: lowest
6672
symfony: '*'
6773
database: sqlite
6874
use-dama: 0
6975
use-migrate: 0
76+
use-phpunit-extension: 0
7077
phpunit: 9
7178
- php: 8.3
7279
deps: lowest
7380
symfony: '*'
7481
database: mysql
7582
use-dama: 1
7683
use-migrate: 0
84+
use-phpunit-extension: 0
7785
phpunit: 9
7886
- php: 8.3
7987
deps: highest
8088
symfony: '*'
8189
database: mysql
8290
use-dama: 1
8391
use-migrate: 1
92+
use-phpunit-extension: 0
8493
phpunit: 9
8594
- php: 8.3
8695
deps: highest
8796
symfony: '*'
8897
database: mysql|mongo
8998
use-dama: 1
9099
use-migrate: 0
100+
use-phpunit-extension: 0
91101
phpunit: 10
92102
- php: 8.3
93103
deps: highest
94104
symfony: '*'
95105
database: mysql|mongo
96106
use-dama: 1
97107
use-migrate: 0
108+
use-phpunit-extension: 0
98109
phpunit: 11
110+
- php: 8.3
111+
deps: highest
112+
symfony: '*'
113+
database: mysql|mongo
114+
use-dama: 1
115+
use-migrate: 0
116+
use-phpunit-extension: 1
117+
phpunit: 11.4
99118
env:
100119
DATABASE_URL: ${{ contains(matrix.database, 'mysql') && 'mysql://root:root@localhost:3306/foundry?serverVersion=5.7.42' || contains(matrix.database, 'pgsql') && 'postgresql://root:root@localhost:5432/foundry?serverVersion=15' || contains(matrix.database, 'sqlite') && 'sqlite:///%kernel.project_dir%/var/data.db' || '' }}
101120
MONGO_URL: ${{ contains(matrix.database, 'mongo') && 'mongodb://127.0.0.1:27017/dbName?compressors=disabled&gssapiServiceName=mongodb' || '' }}
102121
USE_DAMA_DOCTRINE_TEST_BUNDLE: ${{ matrix.use-dama == 1 && contains(matrix.database, 'sql') && 1 || 0 }}
122+
USE_FOUNDRY_PHPUNIT_EXTENSION: ${{ matrix.use-phpunit-extension }}
103123
PHPUNIT_VERSION: ${{ matrix.phpunit }}
104124
services:
105125
postgres:

bin/console

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
<?php
33

44
use Symfony\Bundle\FrameworkBundle\Console\Application;
5-
use Zenstruck\Foundry\Tests\Fixtures\Kernel;
5+
use Zenstruck\Foundry\Tests\Fixture\TestKernel;
66

77
require_once __DIR__ . '/../tests/bootstrap.php';
88

9-
$application = new Application(new Kernel('test', true));
9+
$application = new Application(new TestKernel('test', true));
1010
$application->run();

phpstan.neon

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ parameters:
1414
- identifier: missingType.iterableValue
1515
path: tests/
1616

17+
# We support both PHPUnit
18+
- message: '#Call to function method_exists\(\) with .* will always evaluate to false#'
19+
path: src/Test/Factories.php
20+
1721
excludePaths:
1822
- tests/Fixture/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php
1923
- tests/Fixture/Maker/expected/can_create_factory_interactively.php

phpunit

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,21 @@ if [ "${SHOULD_UPDATE_PHPUNIT}" = "0" ]; then
4949
fi
5050
### <<
5151

52-
### >> guess extensions
53-
EXTENSION=""
52+
### >> actually execute PHPUnit with the right options
53+
DAMA_EXTENSION="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension"
54+
FOUNDRY_EXTENSION="Zenstruck\Foundry\PHPUnit\Extension"
5455

55-
if [ "${USE_DAMA_DOCTRINE_TEST_BUNDLE:-0}" = "1" ]; then
56-
EXTENSION="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension"
56+
if [ "${USE_FOUNDRY_PHPUNIT_EXTENSION:-0}" = "1" ] && [ "${PHPUNIT_VERSION}" != "11.4" ]; then
57+
echo "❌ USE_FOUNDRY_PHPUNIT_EXTENSION could only be used with PHPUNIT_VERSION=11.4";
58+
exit 1;
5759
fi
58-
### <<
5960

60-
### >> actually execute PHPUnit with the right options
6161
case ${PHPUNIT_VERSION} in
6262
"9")
63-
if [ -z "${EXTENSION}" ]; then
64-
vendor/bin/phpunit -c phpunit.xml.dist "$@"
63+
if [ "${USE_DAMA_DOCTRINE_TEST_BUNDLE:-0}" = "1" ]; then
64+
vendor/bin/phpunit -c phpunit.xml.dist --extensions "${DAMA_EXTENSION}" "$@"
6565
else
66-
vendor/bin/phpunit -c phpunit.xml.dist --extensions "${EXTENSION}" "$@"
66+
vendor/bin/phpunit -c phpunit.xml.dist "$@"
6767
fi
6868
;;
6969

@@ -73,11 +73,16 @@ case ${PHPUNIT_VERSION} in
7373
;;
7474

7575
"11"|"11.4")
76-
if [ -z "${EXTENSION}" ]; then
77-
vendor/bin/phpunit -c phpunit-10.xml.dist "$@"
78-
else
79-
vendor/bin/phpunit -c phpunit-10.xml.dist --extension "${EXTENSION}" "$@"
76+
PHPUNIT_EXEC="vendor/bin/phpunit -c phpunit-10.xml.dist $@"
77+
if [ "${USE_DAMA_DOCTRINE_TEST_BUNDLE:-0}" = "1" ]; then
78+
PHPUNIT_EXEC="${PHPUNIT_EXEC} --extension "${DAMA_EXTENSION}""
8079
fi
80+
81+
if [ "${USE_FOUNDRY_PHPUNIT_EXTENSION:-0}" = "1" ]; then
82+
PHPUNIT_EXEC="${PHPUNIT_EXEC} --extension "${FOUNDRY_EXTENSION}""
83+
fi
84+
85+
$PHPUNIT_EXEC
8186
;;
8287
esac
8388
### <<

phpunit-10.xml.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
33
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4-
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
4+
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
55
bootstrap="tests/bootstrap.php"
66
colors="true"
77
failOnRisky="true"

src/Configuration.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ final class Configuration
3333
*/
3434
public $instantiator;
3535

36-
/** @var \Closure():self|self|null */
37-
private static \Closure|self|null $instance = null;
36+
private bool $bootedForDataProvider = false;
37+
38+
private static ?self $instance = null;
3839

3940
/**
4041
* @param InstantiatorCallable $instantiator
@@ -66,23 +67,29 @@ public function assertPersistanceEnabled(): void
6667
}
6768
}
6869

70+
public function inADataProvider(): bool
71+
{
72+
return $this->bootedForDataProvider;
73+
}
74+
6975
public static function instance(): self
7076
{
7177
if (!self::$instance) {
72-
throw new FoundryNotBooted('Foundry is not yet booted. Ensure ZenstruckFoundryBundle is enabled. If in a test, ensure your TestCase has the Factories trait.');
78+
throw new FoundryNotBooted();
7379
}
7480

75-
return \is_callable(self::$instance) ? (self::$instance)() : self::$instance;
81+
return self::$instance;
7682
}
7783

7884
public static function isBooted(): bool
7985
{
8086
return null !== self::$instance;
8187
}
8288

83-
public static function boot(\Closure|self $configuration): void
89+
public static function boot(\Closure|self $configuration, bool $bootForDataProvider = false): void
8490
{
85-
self::$instance = $configuration;
91+
self::$instance = \is_callable($configuration) ? ($configuration)() : $configuration;
92+
self::$instance->bootedForDataProvider = $bootForDataProvider;
8693
}
8794

8895
public static function shutdown(): void

src/Exception/FoundryNotBooted.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@
1616
*/
1717
final class FoundryNotBooted extends \LogicException
1818
{
19+
public function __construct()
20+
{
21+
parent::__construct('Foundry is not yet booted. Ensure ZenstruckFoundryBundle is enabled. If in a test, ensure your TestCase has the Factories trait.');
22+
}
1923
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the zenstruck/foundry package.
7+
*
8+
* (c) Kevin Bond <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Zenstruck\Foundry\PHPUnit;
15+
16+
use PHPUnit\Event;
17+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
18+
use Symfony\Component\HttpKernel\KernelInterface;
19+
use Zenstruck\Foundry\Configuration;
20+
use Zenstruck\Foundry\Test\UnitTestConfig;
21+
22+
/**
23+
* @author Nicolas PHILIPPE <[email protected]>
24+
*/
25+
final class BootFoundryOnDataProviderMethodCalled implements Event\Test\DataProviderMethodCalledSubscriber
26+
{
27+
public function __construct(
28+
private KernelInterface $kernel,
29+
) {
30+
}
31+
32+
public function notify(Event\Test\DataProviderMethodCalled $event): void
33+
{
34+
if (\is_a($event->testMethod()->className(), KernelTestCase::class, allow_string: true)) {
35+
static $kernelIsBooted = false;
36+
37+
if (!$kernelIsBooted) {
38+
$this->kernel->boot();
39+
$kernelIsBooted = true;
40+
}
41+
42+
Configuration::boot(
43+
fn() => $this->kernel->getContainer()->get('.zenstruck_foundry.configuration'),
44+
bootForDataProvider: true
45+
);
46+
} else {
47+
Configuration::boot(UnitTestConfig::build(), bootForDataProvider: true);
48+
}
49+
}
50+
}

src/PHPUnit/Extension.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the zenstruck/foundry package.
7+
*
8+
* (c) Kevin Bond <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Zenstruck\Foundry\PHPUnit;
15+
16+
use PHPUnit\Metadata\Version\ConstraintRequirement;
17+
use PHPUnit\Runner;
18+
use PHPUnit\TextUI;
19+
use Symfony\Component\HttpKernel\KernelInterface;
20+
use Zenstruck\Foundry\Configuration;
21+
22+
final class Extension implements Runner\Extension\Extension
23+
{
24+
public const MIN_PHPUNIT_VERSION = '11.4';
25+
26+
public function bootstrap(
27+
TextUI\Configuration\Configuration $configuration,
28+
Runner\Extension\Facade $facade,
29+
Runner\Extension\ParameterCollection $parameters,
30+
): void {
31+
if (!ConstraintRequirement::from(self::MIN_PHPUNIT_VERSION)->isSatisfiedBy(Runner\Version::id())) {
32+
throw new \LogicException(\sprintf('Your PHPUnit version (%s) is not compatible with the minimum version (%s) needed to use this extension.', Runner\Version::id(), self::MIN_PHPUNIT_VERSION));
33+
}
34+
35+
// shutdown Foundry if for some reason it has been booted before
36+
if (Configuration::isBooted()) {
37+
Configuration::shutdown();
38+
}
39+
40+
$kernel = $this->createKernel();
41+
42+
$facade->registerSubscribers(
43+
new BootFoundryOnDataProviderMethodCalled($kernel),
44+
new ShutdownKernelOnTestSuiteLoaded($kernel)
45+
);
46+
}
47+
48+
/**
49+
* This logic was shamelessly stolen from Symfony's KernelTestCase.
50+
*/
51+
private function createKernel(): KernelInterface
52+
{
53+
if (!isset($_SERVER['KERNEL_CLASS']) && !isset($_ENV['KERNEL_CLASS'])) {
54+
throw new \LogicException('You must set the KERNEL_CLASS environment variable to the fully-qualified class name of your Kernel in phpunit.xml / phpunit.xml.dist.');
55+
}
56+
57+
if (!\class_exists($class = $_ENV['KERNEL_CLASS'] ?? $_SERVER['KERNEL_CLASS'])) {
58+
throw new \RuntimeException(\sprintf('Class "%s" doesn\'t exist or cannot be autoloaded. Check that the KERNEL_CLASS value in phpunit.xml matches the fully-qualified class name of your Kernel.', $class));
59+
}
60+
61+
/**
62+
* @var class-string<KernelInterface> $class
63+
*/
64+
$env = $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? 'test';
65+
$debug = $_ENV['APP_DEBUG'] ?? $_SERVER['APP_DEBUG'] ?? true;
66+
67+
return new $class($env, $debug);
68+
}
69+
}

0 commit comments

Comments
 (0)