Skip to content

Commit 26bb2b4

Browse files
committed
feat: force PHPUnit extension usage
1 parent 5a554e1 commit 26bb2b4

13 files changed

+253
-70
lines changed

phpunit-deprecation-baseline.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
<issue><![CDATA[Since zenstruck/foundry 2.7: Proxy usage is deprecated in PHP 8.4. You should extend directly PersistentObjectFactory in your factories.
1313
Foundry now leverages the native PHP lazy system to auto-refresh objects (it can be enabled with "zenstruck_foundry.enable_auto_refresh_with_lazy_objects" configuration).
1414
See https://github.com/zenstruck/foundry/blob/2.x/UPGRADE-2.7.md to upgrade.]]></issue>
15-
16-
<issue><![CDATA[Support for MySQL < 8 is deprecated and will be removed in DBAL 5 (AbstractMySQLDriver.php:75 called by AbstractDriverMiddleware.php:32, https://github.com/doctrine/dbal/pull/6343, package doctrine/dbal)]]></issue>
15+
<issue><![CDATA[Since zenstruck/foundry 2.7: Trait Zenstruck\Foundry\Test\Factories is deprecated and will be removed in Foundry 3.]]></issue>
16+
<issue><![CDATA[Since zenstruck/foundry 2.7: Not using Foundry's PHPUnit extension is deprecated and will throw an error in Foundry 3.]]></issue>
1717
</line>
1818
</file>
1919
<file path="vendor/doctrine/deprecations/src/Deprecation.php">

src/Configuration.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
use Zenstruck\Foundry\InMemory\CannotEnableInMemory;
2020
use Zenstruck\Foundry\InMemory\InMemoryRepositoryRegistry;
2121
use Zenstruck\Foundry\Persistence\PersistenceManager;
22+
use Zenstruck\Foundry\PHPUnit\FoundryExtension;
23+
use Zenstruck\Foundry\Test\Factories;
2224
use Zenstruck\Foundry\Persistence\Proxy\PersistedObjectsTracker;
2325

2426
/**
@@ -132,6 +134,10 @@ public static function boot(\Closure|self $configuration): void
132134
{
133135
PersistedObjectsTracker::reset();
134136
self::$instance = $configuration;
137+
138+
if (FoundryExtension::shouldBeEnabled()) {
139+
trigger_deprecation('zenstruck/foundry', '2.7', 'Not using Foundry\'s PHPUnit extension is deprecated and will throw an error in Foundry 3.');
140+
}
135141
}
136142

137143
/** @param \Closure():self|self $configuration */

src/Exception/FoundryNotBooted.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,19 @@
1111

1212
namespace Zenstruck\Foundry\Exception;
1313

14+
use Zenstruck\Foundry\PHPUnit\FoundryExtension;
15+
1416
/**
1517
* @author Kevin Bond <kevinbond@gmail.com>
1618
*/
1719
final class FoundryNotBooted extends \LogicException
1820
{
1921
public function __construct()
2022
{
21-
parent::__construct('Foundry is not yet booted. Ensure ZenstruckFoundryBundle is enabled. If in a test, ensure your TestCase has the Factories trait.');
23+
$message = FoundryExtension::isEnabled()
24+
? 'Foundry is not yet booted. Ensure ZenstruckFoundryBundle is enabled. If in a test, ensure Foundry\'s PHPUnit extension is enabled.'
25+
: 'Foundry is not yet booted. Ensure ZenstruckFoundryBundle is enabled. If in a test, ensure your TestCase has the Factories trait.';
26+
27+
parent::__construct($message);
2228
}
2329
}

src/PHPUnit/BootFoundryOnDataProviderMethodCalled.php

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414
namespace Zenstruck\Foundry\PHPUnit;
1515

1616
use PHPUnit\Event;
17+
use PHPUnit\Framework\TestCase;
18+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
1719
use Zenstruck\Foundry\Configuration;
1820
use Zenstruck\Foundry\InMemory\AsInMemoryTest;
21+
use Zenstruck\Foundry\Test\UnitTestConfig;
1922

2023
/**
2124
* @internal
@@ -25,14 +28,38 @@ final class BootFoundryOnDataProviderMethodCalled implements Event\Test\DataProv
2528
{
2629
public function notify(Event\Test\DataProviderMethodCalled $event): void
2730
{
28-
if (\method_exists($event->testMethod()->className(), '_bootForDataProvider')) {
29-
$event->testMethod()->className()::_bootForDataProvider();
30-
}
31+
$this->bootFoundryForDataProvider($event->testMethod()->className());
3132

3233
$testMethod = $event->testMethod();
3334

3435
if (AsInMemoryTest::shouldEnableInMemory($testMethod->className(), $testMethod->methodName())) {
3536
Configuration::instance()->enableInMemory();
3637
}
3738
}
39+
40+
/**
41+
* @param class-string $className
42+
*/
43+
private function bootFoundryForDataProvider(string $className): void
44+
{
45+
if (!\is_subclass_of($className, TestCase::class)) {
46+
return;
47+
}
48+
49+
// unit test
50+
if (!\is_subclass_of($className, KernelTestCase::class)) {
51+
Configuration::bootForDataProvider(UnitTestConfig::build());
52+
53+
return;
54+
}
55+
56+
// integration test
57+
Configuration::bootForDataProvider(static function() use ($className): Configuration {
58+
if (!KernelTestCaseHelper::getContainerForTestClass($className)->has('.zenstruck_foundry.configuration')) {
59+
throw new \LogicException('ZenstruckFoundryBundle is not enabled. Ensure it is added to your config/bundles.php.');
60+
}
61+
62+
return KernelTestCaseHelper::getContainerForTestClass($className)->get('.zenstruck_foundry.configuration'); // @phpstan-ignore return.type
63+
});
64+
}
3865
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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 <kevinbond@gmail.com>
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 PHPUnit\Framework\TestCase;
18+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
19+
use Zenstruck\Foundry\Configuration;
20+
use Zenstruck\Foundry\Test\UnitTestConfig;
21+
22+
/**
23+
* @internal
24+
* @author Nicolas PHILIPPE <nikophil@gmail.com>
25+
*/
26+
final class BootFoundryOnPreparationStarted implements Event\Test\PreparationStartedSubscriber
27+
{
28+
public function notify(Event\Test\PreparationStarted $event): void
29+
{
30+
if (!$event->test()->isTestMethod()) {
31+
return;
32+
}
33+
34+
$this->bootFoundry($event->test()->className());
35+
}
36+
37+
/**
38+
* @param class-string $className
39+
*/
40+
private function bootFoundry(string $className): void
41+
{
42+
if (!\is_subclass_of($className, TestCase::class)) {
43+
return;
44+
}
45+
46+
// unit test
47+
if (!\is_subclass_of($className, KernelTestCase::class)) {
48+
Configuration::boot(UnitTestConfig::build());
49+
50+
return;
51+
}
52+
53+
// integration test
54+
Configuration::boot(static function() use ($className): Configuration {
55+
if (!KernelTestCaseHelper::getContainerForTestClass($className)->has('.zenstruck_foundry.configuration')) {
56+
throw new \LogicException('ZenstruckFoundryBundle is not enabled. Ensure it is added to your config/bundles.php.');
57+
}
58+
59+
return KernelTestCaseHelper::getContainerForTestClass($className)->get('.zenstruck_foundry.configuration'); // @phpstan-ignore return.type
60+
});
61+
}
62+
}

src/PHPUnit/FoundryExtension.php

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,62 @@
2222
* @internal
2323
* @author Nicolas PHILIPPE <nikophil@gmail.com>
2424
*/
25-
final class FoundryExtension implements Runner\Extension\Extension
26-
{
27-
public function bootstrap(
28-
TextUI\Configuration\Configuration $configuration,
29-
Runner\Extension\Facade $facade,
30-
Runner\Extension\ParameterCollection $parameters,
31-
): void {
32-
// shutdown Foundry if for some reason it has been booted before
33-
if (Configuration::isBooted()) {
34-
Configuration::shutdown();
25+
26+
if (interface_exists(Runner\Extension\Extension::class)) {
27+
final class FoundryExtension implements Runner\Extension\Extension
28+
{
29+
private static bool $enabled = false;
30+
31+
public function bootstrap(
32+
TextUI\Configuration\Configuration $configuration,
33+
Runner\Extension\Facade $facade,
34+
Runner\Extension\ParameterCollection $parameters,
35+
): void {
36+
// shutdown Foundry if for some reason it has been booted before
37+
if (Configuration::isBooted()) {
38+
Configuration::shutdown();
39+
}
40+
41+
$subscribers = [
42+
new BuildStoryOnTestPrepared(),
43+
new EnableInMemoryBeforeTest(),
44+
new DisplayFakerSeedOnTestSuiteFinished(),
45+
new BootFoundryOnPreparationStarted(),
46+
new ShutdownFoundryOnTestFinished(),
47+
];
48+
49+
if (ConstraintRequirement::from('>=11.4')->isSatisfiedBy(Runner\Version::id())) {
50+
// those deal with data provider events which can be useful only if PHPUnit >=11.4 is used
51+
$subscribers[] = new BootFoundryOnDataProviderMethodCalled();
52+
$subscribers[] = new ShutdownFoundryOnDataProviderMethodFinished();
53+
}
54+
55+
$facade->registerSubscribers(...$subscribers);
56+
57+
self::$enabled = true;
3558
}
3659

37-
$subscribers = [
38-
new BuildStoryOnTestPrepared(),
39-
new EnableInMemoryBeforeTest(),
40-
new DisplayFakerSeedOnTestSuiteFinished(),
41-
];
60+
public static function shouldBeEnabled(): bool
61+
{
62+
return !self::isEnabled() && ConstraintRequirement::from('>=10')->isSatisfiedBy(Runner\Version::id());
63+
}
4264

43-
if (ConstraintRequirement::from('>=11.4')->isSatisfiedBy(Runner\Version::id())) {
44-
// those deal with data provider events which can be useful only if PHPUnit >=11.4 is used
45-
$subscribers[] = new BootFoundryOnDataProviderMethodCalled();
46-
$subscribers[] = new ShutdownFoundryOnDataProviderMethodFinished();
65+
public static function isEnabled(): bool
66+
{
67+
return self::$enabled;
68+
}
69+
}
70+
} else {
71+
final class FoundryExtension
72+
{
73+
public static function shouldBeEnabled(): bool
74+
{
75+
return false;
4776
}
4877

49-
$facade->registerSubscribers(...$subscribers);
78+
public static function isEnabled(): bool
79+
{
80+
return false;
81+
}
5082
}
5183
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace Zenstruck\Foundry\PHPUnit;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
7+
use Symfony\Component\DependencyInjection\Container;
8+
9+
/**
10+
* @internal
11+
*/
12+
final class KernelTestCaseHelper
13+
{
14+
/**
15+
* @param class-string $class
16+
*/
17+
public static function getContainerForTestClass(string $class): Container
18+
{
19+
if (!\is_subclass_of($class, KernelTestCase::class)) {
20+
throw new \LogicException(\sprintf('Class "%s" must extend "%s".', $class, KernelTestCase::class));
21+
}
22+
23+
return (\Closure::bind(
24+
fn() => $class::getContainer(),
25+
newThis: null,
26+
newScope: $class,
27+
))();
28+
}
29+
30+
/**
31+
* @param class-string $class
32+
*/
33+
public static function tearDownClass(string $class): void
34+
{
35+
if (!\is_subclass_of($class, TestCase::class)) {
36+
throw new \LogicException(\sprintf('Class "%s" must extend "%s".', $class, TestCase::class));
37+
}
38+
39+
(\Closure::bind(
40+
fn() => $class::tearDownAfterClass(),
41+
newThis: null,
42+
newScope: $class,
43+
))();
44+
}
45+
}

src/PHPUnit/ShutdownFoundryOnDataProviderMethodFinished.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace Zenstruck\Foundry\PHPUnit;
1515

1616
use PHPUnit\Event;
17+
use Zenstruck\Foundry\Configuration;
1718

1819
/**
1920
* @internal
@@ -23,8 +24,8 @@ final class ShutdownFoundryOnDataProviderMethodFinished implements Event\Test\Da
2324
{
2425
public function notify(Event\Test\DataProviderMethodFinished $event): void
2526
{
26-
if (\method_exists($event->testMethod()->className(), '_shutdownAfterDataProvider')) {
27-
$event->testMethod()->className()::_shutdownAfterDataProvider();
28-
}
27+
KernelTestCaseHelper::tearDownClass($event->testMethod()->className());
28+
29+
Configuration::shutdown();
2930
}
3031
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 <kevinbond@gmail.com>
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 Zenstruck\Foundry\Configuration;
18+
19+
/**
20+
* @internal
21+
* @author Nicolas PHILIPPE <nikophil@gmail.com>
22+
*/
23+
final class ShutdownFoundryOnTestFinished implements Event\Test\FinishedSubscriber
24+
{
25+
public function notify(Event\Test\Finished $event): void
26+
{
27+
Configuration::shutdown();
28+
}
29+
}

0 commit comments

Comments
 (0)