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 316d3c7 commit bdd057d
Show file tree
Hide file tree
Showing 15 changed files with 389 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
7 changes: 7 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
\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 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.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/Integration/ForceFactoriesTraitUsage</exclude>
</testsuite>
<testsuite name="migrate">
<file>tests/Integration/Migration/ResetDatabaseWithMigrationTest.php</file>
Expand Down
3 changes: 3 additions & 0 deletions src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Zenstruck\Foundry;

use Faker;
use Zenstruck\Foundry\Exception\FactoriesTraitNotUsed;
use Zenstruck\Foundry\Exception\FoundryNotBooted;
use Zenstruck\Foundry\Exception\PersistenceDisabled;
use Zenstruck\Foundry\Exception\PersistenceNotAvailable;
Expand Down Expand Up @@ -90,6 +91,8 @@ public static function instance(): self
throw new FoundryNotBooted();
}

FactoriesTraitNotUsed::throwIfComingFromKernelTestCaseWithoutFactoriesTrait();

return \is_callable(self::$instance) ? (self::$instance)() : self::$instance;
}

Expand Down
67 changes: 67 additions & 0 deletions src/Exception/FactoriesTraitNotUsed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?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
*/
private 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);
trigger_deprecation(
'zenstruck/foundry',
'2.4',
'In order to use Foundry, you must use the trait "%s" in your "%s" tests. This will throw an exception in 3.0.',
KernelTestCase::class,
$class
);
}
}
}
1 change: 1 addition & 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
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,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 PHPUnit\Framework\Attributes\RequiresPhpunit;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory;

#[RequiresPhpunit('>=11.0')]
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()->get('.zenstruck_foundry.configuration');

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 PHPUnit\Framework\Attributes\RequiresPhpunit;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory;

#[RequiresPhpunit('>=11.0')]
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()->get('.zenstruck_foundry.configuration');

Object1Factory::createOne();

$this->expectNotToPerformAssertions();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?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 PHPUnit\Framework\Attributes\RequiresPhpunit;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Test\Factories;
use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory;

#[RequiresPhpunit('>=11.0')]
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()->get('.zenstruck_foundry.configuration');

Object1Factory::createOne();

$this->expectNotToPerformAssertions();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?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 PHPUnit\Framework\Attributes\IgnoreDeprecations;
use PHPUnit\Framework\Attributes\RequiresPhpunit;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Exception\FactoriesTraitNotUsed;
use Zenstruck\Foundry\Test\ResetDatabase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory;

#[RequiresPhpunit('>=11.0')]
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]
#[IgnoreDeprecations]
public function using_foundry_should_throw(): void
{
$this->expectUserDeprecationMessageMatches('/In order to use Foundry, you must use the trait/');

Object1Factory::createOne();
}
}
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 PHPUnit\Framework\Attributes\IgnoreDeprecations;
use PHPUnit\Framework\Attributes\RequiresPhpunit;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory;

#[RequiresPhpunit('>=11.0')]
final class KernelTestCaseWithoutFactoriesTraitTest extends KernelTestCase
{
#[Test]
#[IgnoreDeprecations]
public function using_foundry_without_trait_should_throw(): void
{
$this->expectUserDeprecationMessageMatches('/In order to use Foundry, you must use the trait/');

Object1Factory::createOne();
}

#[Test]
#[IgnoreDeprecations]
public function using_foundry_without_trait_should_throw_even_when_kernel_is_booted(): void
{
$this->expectUserDeprecationMessageMatches('/In order to use Foundry, you must use the trait/');

self::getContainer()->get('.zenstruck_foundry.configuration');

Object1Factory::createOne();
}

#[Test]
public function booting_kernel_without_using_foundry_and_without_factories_trait_should_not_throw(): void
{
self::getContainer()->get('.zenstruck_foundry.configuration');

$this->expectNotToPerformAssertions();
}
}
Loading

0 comments on commit bdd057d

Please sign in to comment.