Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
25 changes: 24 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1765,13 +1765,36 @@ Then, by default, before each test, it resets the schema using ``doctrine:schema

.. tip::

Create a base TestCase for tests using factories to avoid adding the attribute to every TestCase.
Create a base TestCase for tests using factories to avoid adding the attributes to every TestCase.

.. tip::

If your tests :ref:`are not persisting <without-persisting>` the objects they create, the ``ResetDatabase``
attribute is not required.

Automatic Database Reset for Base Test Classes
..............................................

Instead of adding the ``#[ResetDatabase]`` attribute to every test class, you can configure Foundry to
automatically reset the database for all tests extending ``Symfony\Bundle\FrameworkBundle\Test\KernelTestCase``.

.. configuration-block::

.. code-block:: xml

<!-- phpunit.xml -->
<phpunit>
<extensions>
<bootstrap class="Zenstruck\Foundry\PHPUnit\FoundryExtension">
<parameter name="enabled-auto-reset" value="true"/>
</bootstrap>
</extensions>
</phpunit>

.. versionadded:: 2.9

Automatic database reset for base test classes was added in Foundry 2.9 and requires at least PHPUnit 10.

By default, ``ResetDatabase`` resets the default configured connection's database and default configured object manager's
schema. To customize the connection's and object manager's to be reset (or reset multiple connections/managers), use the
bundle's configuration:
Expand Down
9 changes: 9 additions & 0 deletions src/PHPUnit/AttributeReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,13 @@ public static function collectAttributesFromClassAndParents(string $attributeCla
),
];
}

/**
* @param class-string<object> $class
* @param class-string<object> $attributeClass
*/
public static function classOrParentsHasAttribute(string $class, string $attributeClass): bool
{
return self::collectAttributesFromClassAndParents($attributeClass, new \ReflectionClass($class)) !== [];
}
}
1 change: 0 additions & 1 deletion src/PHPUnit/BootFoundryOnTestPrepared.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

use PHPUnit\Event;
use PHPUnit\Framework\TestCase;
use PHPUnit\Util\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Configuration;
use Zenstruck\Foundry\Test\UnitTestConfig;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Zenstruck\Foundry\PHPUnit\DataProvider;

use PHPUnit\Event;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Configuration;
use Zenstruck\Foundry\PHPUnit\KernelTestCaseHelper;

Expand All @@ -23,7 +24,11 @@ final class ShutdownFoundryOnDataProviderMethodFinished implements Event\Test\Da
{
public function notify(Event\Test\DataProviderMethodFinished $event): void
{
KernelTestCaseHelper::tearDownClass($event->testMethod()->className());
$class = $event->testMethod()->className();

if (\is_subclass_of($class, KernelTestCase::class)) {
KernelTestCaseHelper::ensureKernelShutdown($class);
}

Configuration::shutdown();
}
Expand Down
9 changes: 7 additions & 2 deletions src/PHPUnit/FoundryExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
if (\interface_exists(Runner\Extension\Extension::class)) {
final class FoundryExtension implements Runner\Extension\Extension
{
public const PARAMETER_AUTO_RESET_DATABASE_CLASS = 'enabled-auto-reset';

private static bool $enabled = false;

public function bootstrap(
Expand All @@ -44,15 +46,18 @@ public function bootstrap(
Configuration::shutdown();
}

$autoResetEnabled = $parameters->has(self::PARAMETER_AUTO_RESET_DATABASE_CLASS)
&& $parameters->get(self::PARAMETER_AUTO_RESET_DATABASE_CLASS) === 'true';

// ⚠️ order matters within each event
$subscribers = [
Event\TestSuite\Started::class => [new ResetDatabaseOnTestSuiteStarted()],
Event\TestSuite\Started::class => [new ResetDatabaseOnTestSuiteStarted($autoResetEnabled)],
Event\Test\DataProviderMethodCalled::class => [new BootFoundryOnDataProviderMethodCalled()],
Event\Test\DataProviderMethodFinished::class => [new ShutdownFoundryOnDataProviderMethodFinished()],
Event\Test\Prepared::class => [
new BootFoundryOnTestPrepared(),
new EnableInMemoryOnTestPrepared(),
new ResetDatabaseOnTestPrepared(),
new ResetDatabaseOnTestPrepared($autoResetEnabled),
new BuildStoryOnTestPrepared(),
new TriggerDataProviderPersistenceOnTestPrepared(),
],
Expand Down
29 changes: 9 additions & 20 deletions src/PHPUnit/KernelTestCaseHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,19 @@ public static function getContainer(string $class): Container
/**
* @param class-string $class
*/
public static function tearDownClass(string $class): void
public static function ensureKernelShutdown(string $class): void
{
if (!\is_subclass_of($class, TestCase::class)) {
throw new \LogicException(\sprintf('Class "%s" must extend "%s".', $class, TestCase::class));
if (!\is_subclass_of($class, KernelTestCase::class)) {
throw new \LogicException(\sprintf('Class "%s" must extend "%s".', $class, KernelTestCase::class));
}

(\Closure::bind(
fn() => $class::tearDownAfterClass(),
static function () {
static::ensureKernelShutdown();
static::$class = null;
static::$kernel = null;
static::$booted = false;
},
newThis: null,
newScope: $class,
))();
Expand All @@ -68,20 +73,4 @@ public static function bootKernel(string $class): KernelInterface
newScope: $class,
))();
}

/**
* @param class-string $class
*/
public static function ensureKernelShutdown(string $class): void
{
if (!\is_subclass_of($class, KernelTestCase::class)) {
throw new \LogicException(\sprintf('Class "%s" must extend "%s".', $class, KernelTestCase::class));
}

(\Closure::bind(
fn() => $class::ensureKernelShutdown(),
newThis: null,
newScope: $class,
))();
}
}
45 changes: 38 additions & 7 deletions src/PHPUnit/ResetDatabase/ResetDatabaseOnTestPrepared.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
namespace Zenstruck\Foundry\PHPUnit\ResetDatabase;

use PHPUnit\Event;
use PHPUnit\Event\Code\TestMethod;
use PHPUnit\Event\Test\Prepared;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Attribute\ResetDatabase;
use Zenstruck\Foundry\InMemory\AsInMemoryTest;
use Zenstruck\Foundry\Persistence\ResetDatabase\ResetDatabaseManager;
use Zenstruck\Foundry\PHPUnit\AttributeReader;
use Zenstruck\Foundry\PHPUnit\KernelTestCaseHelper;
Expand All @@ -22,27 +25,55 @@
* @internal
* @author Nicolas PHILIPPE <[email protected]>
*/
final class ResetDatabaseOnTestPrepared implements Event\Test\PreparedSubscriber
final readonly class ResetDatabaseOnTestPrepared implements Event\Test\PreparedSubscriber
{
public function __construct(
private bool $autoResetEnabled = false,
) {
}

public function notify(Prepared $event): void
{
$test = $event->test();

if (!$test->isTestMethod()) {
return;
}
/** @var Event\Code\TestMethod $test */
$resetDatabaseAttributes = AttributeReader::collectAttributesFromClassAndParents(
ResetDatabase::class,
new \ReflectionClass($test->className())
);

if ([] === $resetDatabaseAttributes || ResetDatabaseManager::canSkipSchemaReset()) {
if (!$this->shouldReset($test)) {
return;
}

ResetDatabaseManager::resetBeforeEachTest(
KernelTestCaseHelper::bootKernel($test->className()),
);

KernelTestCaseHelper::ensureKernelShutdown($test->className());
}

private function shouldReset(TestMethod $test): bool
{
$hasResetDatabaseAttribute = AttributeReader::classOrParentsHasAttribute($test->className(), ResetDatabase::class);

if (!is_subclass_of($test->className(), KernelTestCase::class)) {
if ($hasResetDatabaseAttribute) {
throw new \LogicException(\sprintf('Class "%s" cannot use attribute #[ResetDatabase] if it does not extend "%s".', $test->className(), KernelTestCase::class));
}

return false;
}

if (
AsInMemoryTest::shouldEnableInMemory($test->className(), $test->methodName())
|| ResetDatabaseManager::canSkipSchemaReset()
) {
return false;
}

if ($this->autoResetEnabled) {
return true;
}

return $hasResetDatabaseAttribute;
}
}
38 changes: 28 additions & 10 deletions src/PHPUnit/ResetDatabase/ResetDatabaseOnTestSuiteStarted.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Zenstruck\Foundry\PHPUnit\ResetDatabase;

use PHPUnit\Event;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Attribute\ResetDatabase;
use Zenstruck\Foundry\Persistence\ResetDatabase\ResetDatabaseManager;
use Zenstruck\Foundry\PHPUnit\AttributeReader;
Expand All @@ -21,8 +22,13 @@
* @internal
* @author Nicolas PHILIPPE <[email protected]>
*/
final class ResetDatabaseOnTestSuiteStarted implements Event\TestSuite\StartedSubscriber
final readonly class ResetDatabaseOnTestSuiteStarted implements Event\TestSuite\StartedSubscriber
{
public function __construct(
private bool $autoResetEnabled = false,
) {
}

public function notify(Event\TestSuite\Started $event): void
{
if (!$event->testSuite()->isForTestClass()) {
Expand All @@ -31,21 +37,33 @@ public function notify(Event\TestSuite\Started $event): void

$testClassName = $event->testSuite()->name();

if (!\class_exists($testClassName)) {
if (
!\class_exists($testClassName)
|| !$this->shouldReset($testClassName)
) {
return;
}

$resetDatabaseAttributes = AttributeReader::collectAttributesFromClassAndParents(
ResetDatabase::class,
new \ReflectionClass($testClassName)
ResetDatabaseManager::resetBeforeFirstTest(
KernelTestCaseHelper::bootKernel($testClassName),
);

if ([] === $resetDatabaseAttributes) {
return;
KernelTestCaseHelper::ensureKernelShutdown($testClassName);
}

/**
* @param class-string $testClassName
*/
private function shouldReset(string $testClassName): bool
{
if (!is_subclass_of($testClassName, KernelTestCase::class)) {
return false;
}

ResetDatabaseManager::resetBeforeFirstTest(
KernelTestCaseHelper::bootKernel($testClassName),
);
if ($this->autoResetEnabled) {
return true;
}

return AttributeReader::classOrParentsHasAttribute($testClassName, ResetDatabase::class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@
namespace Zenstruck\Foundry\Tests\Integration\Attribute\WithStory;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Attribute\ResetDatabase;
use Zenstruck\Foundry\Attribute\WithStory;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Stories\EntityStory;
use Zenstruck\Foundry\Tests\Integration\RequiresORM;

/**
* @author Nicolas PHILIPPE <[email protected]>
*/
#[WithStory(EntityStory::class)]
#[ResetDatabase]
abstract class ParentClassWithStoryAttributeTestCase extends KernelTestCase
{
use RequiresORM, ResetDatabase;
use RequiresORM;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
use PHPUnit\Framework\Attributes\RequiresPhpunitExtension;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Attribute\ResetDatabase;
use Zenstruck\Foundry\Attribute\WithStory;
use Zenstruck\Foundry\PHPUnit\FoundryExtension;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\GenericEntityFactory;
use Zenstruck\Foundry\Tests\Fixture\Stories\EntityPoolStory;
use Zenstruck\Foundry\Tests\Fixture\Stories\EntityStory;
Expand All @@ -32,9 +32,10 @@
#[RequiresPhpunit('>=11.0')]
#[RequiresPhpunitExtension(FoundryExtension::class)]
#[WithStory(EntityStory::class)]
#[ResetDatabase]
final class WithStoryOnClassTest extends KernelTestCase
{
use RequiresORM, ResetDatabase;
use RequiresORM;

#[Test]
public function can_use_story_in_attribute(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
use PHPUnit\Framework\Attributes\RequiresPhpunitExtension;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Attribute\ResetDatabase;
use Zenstruck\Foundry\Attribute\WithStory;
use Zenstruck\Foundry\PHPUnit\FoundryExtension;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\GenericEntityFactory;
use Zenstruck\Foundry\Tests\Fixture\Stories\EntityPoolStory;
use Zenstruck\Foundry\Tests\Fixture\Stories\EntityStory;
Expand All @@ -32,9 +32,10 @@
*/
#[RequiresPhpunit('>=11.0')]
#[RequiresPhpunitExtension(FoundryExtension::class)]
#[ResetDatabase]
final class WithStoryOnMethodTest extends KernelTestCase
{
use RequiresORM, ResetDatabase;
use RequiresORM;

#[Test]
#[WithStory(EntityStory::class)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
use PHPUnit\Framework\Attributes\RequiresPhpunitExtension;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Attribute\ResetDatabase;
use Zenstruck\Foundry\InMemory\AsInMemoryTest;
use Zenstruck\Foundry\Persistence\PersistentObjectFactory;
use Zenstruck\Foundry\Persistence\ProxyGenerator;
use Zenstruck\Foundry\PHPUnit\FoundryExtension;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Entity\Contact;
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Contact\ContactFactory;
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Contact\ProxyContactFactory;
Expand All @@ -40,10 +40,10 @@
*/
#[RequiresPhpunit('>=11.4')]
#[RequiresPhpunitExtension(FoundryExtension::class)]
#[ResetDatabase]
final class DataProviderWithInMemoryTest extends KernelTestCase
{
use RequiresORM; // needed to use the entity manager
use ResetDatabase;

private InMemoryContactRepository $contactRepository;

Expand Down
Loading
Loading