Skip to content

Commit

Permalink
feat: throw an error if Factories trait is not used in a KernelTestCase
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil committed Jan 9, 2025
1 parent 3e9650a commit d61193a
Show file tree
Hide file tree
Showing 19 changed files with 448 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/.phpunit.cache
/vendor/
/bin/tools/*/vendor/
/bin/tools/php-cs-fixer/composer.lock
/bin/tools/csfixer
/build/
/.php-cs-fixer.cache
/.phpunit.result.cache
Expand Down
11 changes: 11 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@
\file_get_contents('https://raw.githubusercontent.com/zenstruck/.github/main/.php-cs-fixer.dist.php')
);

$finder = PhpCsFixer\Finder::create()
->in([__DIR__.'/src', __DIR__.'/tests'])

// we don't want to use @test annotations, to prevent deprecations notices,
// which would make the .phpt test fail with recent PHPUnit versions
->notName('KernelTestCaseUsingWithStoryWithoutFactoriesTraitTest.php')

// we want to keep the traits in "wrong order"
->notName('KernelTestCaseWithBothTraitsInWrongOrderTest.php')
;

try {
return require $file;
} finally {
Expand Down
1 change: 1 addition & 0 deletions phpunit-10.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<testsuite name="main">
<directory>tests</directory>
<exclude>tests/Integration/Migration/ResetDatabaseWithMigrationTest.php</exclude>
<exclude>tests/Fixture</exclude>
</testsuite>
<testsuite name="migrate">
<file>tests/Integration/Migration/ResetDatabaseWithMigrationTest.php</file>
Expand Down
1 change: 1 addition & 0 deletions phpunit-paratest.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<testsuite name="main">
<directory>tests</directory>
<exclude>tests/Integration/Migration/ResetDatabaseWithMigrationTest.php</exclude>
<exclude>tests/Fixture</exclude>
</testsuite>
</testsuites>
<source ignoreSuppressionOfDeprecations="true">
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<testsuite name="main">
<directory>tests</directory>
<exclude>tests/Integration/Migration/ResetDatabaseWithMigrationTest.php</exclude>
<exclude>tests/Fixture</exclude>
</testsuite>
<testsuite name="migrate">
<file>tests/Integration/Migration/ResetDatabaseWithMigrationTest.php</file>
Expand Down
2 changes: 2 additions & 0 deletions src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ public static function isBooted(): bool
/** @param \Closure():self|self $configuration */
public static function boot(\Closure|self $configuration): void
{
// self::throwIfComingFromKernelTestCaseWithoutFactoriesTrait();

self::$instance = $configuration;
}

Expand Down
58 changes: 58 additions & 0 deletions src/Exception/FactoriesTraitNotUsed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

/*
* This file is part of the zenstruck/foundry package.
*
* (c) Kevin Bond <kevinbond@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Zenstruck\Foundry\Exception;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Test\Factories;

/**
* @author Nicolas PHILIPPE <nikophil@gmail.com>
*/
final class FactoriesTraitNotUsed extends \LogicException
{
/**
* @param class-string<KernelTestCase> $class
*/
public function __construct(string $class)
{
parent::__construct(\sprintf('You must use the trait "%s" in "%s" in order to use Foundry.', Factories::class, $class));
}

public static function throwIfComingFromKernelTestCaseWithoutFactoriesTrait(): void
{
$backTrace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); // @phpstan-ignore ekinoBannedCode.function

foreach ($backTrace as $trace) {
if (
'->' === ($trace['type'] ?? null)
&& isset($trace['class'])
&& KernelTestCase::class !== $trace['class']
&& \is_a($trace['class'], KernelTestCase::class, allow_string: true)
&& !(new \ReflectionClass($trace['class']))->hasMethod('_bootFoundry')
) {
self::throwIfClassDoesNotHaveFactoriesTrait($trace['class']);
}
}
}

/**
* @param class-string<KernelTestCase> $class
*/
public static function throwIfClassDoesNotHaveFactoriesTrait(string $class): void
{
if (!(new \ReflectionClass($class))->hasMethod('_bootFoundry')) {
throw new self($class);
}
}
}
3 changes: 3 additions & 0 deletions src/ObjectFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Zenstruck\Foundry;

use Zenstruck\Foundry\Exception\FactoriesTraitNotUsed;
use Zenstruck\Foundry\Object\Instantiator;

/**
Expand Down Expand Up @@ -43,6 +44,8 @@ abstract public static function class(): string;
*/
public function create(callable|array $attributes = []): object
{
FactoriesTraitNotUsed::throwIfComingFromKernelTestCaseWithoutFactoriesTrait();

$parameters = $this->normalizeAttributes($attributes);

foreach ($this->beforeInstantiate as $hook) {
Expand Down
3 changes: 3 additions & 0 deletions src/PHPUnit/BuildStoryOnTestPrepared.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use PHPUnit\Event;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Attribute\WithStory;
use Zenstruck\Foundry\Exception\FactoriesTraitNotUsed;

/**
* @internal
Expand Down Expand Up @@ -46,6 +47,8 @@ public function notify(Event\Test\Prepared $event): void
throw new \InvalidArgumentException(\sprintf('The test class "%s" must extend "%s" to use the "%s" attribute.', $test->className(), KernelTestCase::class, WithStory::class));
}

FactoriesTraitNotUsed::throwIfClassDoesNotHaveFactoriesTrait($test->className());

foreach ($withStoryAttributes as $withStoryAttribute) {
$withStoryAttribute->newInstance()->story::load();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

/*
* This file is part of the zenstruck/foundry package.
*
* (c) Kevin Bond <kevinbond@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Fixture\ForceFactoriesTraitUsage;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Attribute\WithStory;
use Zenstruck\Foundry\Tests\Fixture\Stories\ObjectStory;

#[WithStory(ObjectStory::class)]
final class KernelTestCaseUsingWithStoryWithoutFactoriesTraitTest extends KernelTestCase
{
public function test_using_story_with_attribute_without_factories_trait_should_throw(): void
{
$this->expectNotToPerformAssertions();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

/*
* This file is part of the zenstruck/foundry package.
*
* (c) Kevin Bond <kevinbond@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory;

final class KernelTestCaseWithBothTraitsInWrongOrderTest extends KernelTestCase
{
use ResetDatabase, Factories;

/**
* @test
*/
public function should_not_throw(): void
{
Object1Factory::createOne();

$this->expectNotToPerformAssertions();
}

/**
* @test
*/
public function should_not_throw_even_when_kernel_is_booted(): void
{
self::getContainer();

Object1Factory::createOne();

$this->expectNotToPerformAssertions();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

/*
* This file is part of the zenstruck/foundry package.
*
* (c) Kevin Bond <kevinbond@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory;

final class KernelTestCaseWithBothTraitsTest extends KernelTestCase
{
use Factories, ResetDatabase;

/**
* @test
*/
public function should_not_throw(): void
{
Object1Factory::createOne();

$this->expectNotToPerformAssertions();
}

/**
* @test
*/
public function should_not_throw_even_when_kernel_is_booted(): void
{
self::getContainer();

Object1Factory::createOne();

$this->expectNotToPerformAssertions();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

/*
* This file is part of the zenstruck/foundry package.
*
* (c) Kevin Bond <kevinbond@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory;

final class KernelTestCaseWithFactoriesTraitTest extends KernelTestCase
{
use Factories;

/**
* @test
*/
public function should_not_throw(): void
{
Object1Factory::createOne();

$this->expectNotToPerformAssertions();
}

/**
* @test
*/
public function should_not_throw_even_when_kernel_is_booted(): void
{
self::getContainer();

Object1Factory::createOne();

$this->expectNotToPerformAssertions();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

/*
* This file is part of the zenstruck/foundry package.
*
* (c) Kevin Bond <kevinbond@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Exception\FactoriesTraitNotUsed;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory;

final class KernelTestCaseWithOnlyResetDatabaseTraitTest extends KernelTestCase
{
use ResetDatabase;

/**
* @test
*/
public function not_using_foundry_should_not_throw(): void
{
$this->expectNotToPerformAssertions();
}

/**
* @test
*/
public function not_using_foundry_should_not_throw_even_when_container_is_used(): void
{
self::getContainer()->get('.zenstruck_foundry.configuration');

$this->expectNotToPerformAssertions();
}

/**
* @test
*/
public function using_foundry_should_throw(): void
{
$this->expectException(FactoriesTraitNotUsed::class);

Object1Factory::createOne();
}
}
Loading

0 comments on commit d61193a

Please sign in to comment.