Skip to content

Commit

Permalink
feat: allow to use Factory::create() in data provider
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil committed Aug 9, 2024
1 parent 2730f09 commit a1c9816
Show file tree
Hide file tree
Showing 31 changed files with 830 additions and 168 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ DATABASE_URL="mysql://root:[email protected]:3307/foundry_test?serverVersion=5.7.42
MONGO_URL="mongodb://127.0.0.1:27018/dbName?compressors=disabled&gssapiServiceName=mongodb"
DATABASE_RESET_MODE="schema"
USE_DAMA_DOCTRINE_TEST_BUNDLE="0"
USE_FOUNDRY_PHPUNIT_EXTENSION="0"
PHPUNIT_VERSION="9"
22 changes: 21 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
tests:
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)' }}
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)' || '' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand All @@ -19,6 +19,7 @@ jobs:
database: [ mysql, mongo ]
use-dama: [ 1 ]
use-migrate: [ 0 ]
use-phpunit-extension: [ 0 ]
phpunit: [ 9 ]
exclude:
- php: 8.1
Expand All @@ -32,74 +33,93 @@ jobs:
database: none
use-dama: 1
use-migrate: 0
use-phpunit-extension: 0
phpunit: 9
- php: 8.3
deps: highest
symfony: '*'
database: mysql|mongo
use-dama: 1
use-migrate: 0
use-phpunit-extension: 0
phpunit: 9
- php: 8.3
deps: highest
symfony: '*'
database: pgsql|mongo
use-dama: 1
use-migrate: 0
use-phpunit-extension: 0
phpunit: 9
- php: 8.3
deps: highest
symfony: '*'
database: pgsql
use-dama: 0
use-migrate: 0
use-phpunit-extension: 0
phpunit: 9
- php: 8.3
deps: highest
symfony: '*'
database: sqlite
use-dama: 0
use-migrate: 0
use-phpunit-extension: 0
phpunit: 9
- php: 8.3
deps: lowest
symfony: '*'
database: sqlite
use-dama: 0
use-migrate: 0
use-phpunit-extension: 0
phpunit: 9
- php: 8.3
deps: lowest
symfony: '*'
database: mysql
use-dama: 1
use-migrate: 0
use-phpunit-extension: 0
phpunit: 9
- php: 8.3
deps: highest
symfony: '*'
database: mysql
use-dama: 1
use-migrate: 1
use-phpunit-extension: 0
phpunit: 9
- php: 8.3
deps: highest
symfony: '*'
database: mysql|mongo
use-dama: 1
use-migrate: 0
use-phpunit-extension: 0
phpunit: 10
- php: 8.3
deps: highest
symfony: '*'
database: mysql|mongo
use-dama: 1
use-migrate: 0
use-phpunit-extension: 0
phpunit: 11
- php: 8.3
deps: highest
symfony: '*'
database: mysql|mongo
use-dama: 1
use-migrate: 0
use-phpunit-extension: 1
phpunit: 11.4
env:
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' || '' }}
MONGO_URL: ${{ contains(matrix.database, 'mongo') && 'mongodb://127.0.0.1:27017/dbName?compressors=disabled&gssapiServiceName=mongodb' || '' }}
USE_DAMA_DOCTRINE_TEST_BUNDLE: ${{ matrix.use-dama == 1 && contains(matrix.database, 'sql') && 1 || 0 }}
USE_FOUNDRY_PHPUNIT_EXTENSION: ${{ matrix.use-phpunit-extension }}
PHPUNIT_VERSION: ${{ matrix.phpunit }}
services:
postgres:
Expand Down
4 changes: 2 additions & 2 deletions bin/console
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<?php

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Zenstruck\Foundry\Tests\Fixtures\Kernel;
use Zenstruck\Foundry\Tests\Fixture\TestKernel;

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

$application = new Application(new Kernel('test', true));
$application = new Application(new TestKernel('test', true));
$application->run();
4 changes: 4 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ parameters:
- identifier: missingType.iterableValue
path: tests/

# We support both PHPUnit
- message: '#Call to function method_exists\(\) with .* will always evaluate to false#'
path: src/Test/Factories.php

excludePaths:
- tests/Fixture/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php
- tests/Fixture/Maker/expected/can_create_factory_interactively.php
Expand Down
31 changes: 18 additions & 13 deletions phpunit
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,21 @@ if [ "${SHOULD_UPDATE_PHPUNIT}" = "0" ]; then
fi
### <<

### >> guess extensions
EXTENSION=""
### >> actually execute PHPUnit with the right options
DAMA_EXTENSION="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension"
FOUNDRY_EXTENSION="Zenstruck\Foundry\PHPUnit\Extension"

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

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

Expand All @@ -73,11 +73,16 @@ case ${PHPUNIT_VERSION} in
;;

"11"|"11.4")
if [ -z "${EXTENSION}" ]; then
vendor/bin/phpunit -c phpunit-10.xml.dist "$@"
else
vendor/bin/phpunit -c phpunit-10.xml.dist --extension "${EXTENSION}" "$@"
PHPUNIT_EXEC="vendor/bin/phpunit -c phpunit-10.xml.dist $@"
if [ "${USE_DAMA_DOCTRINE_TEST_BUNDLE:-0}" = "1" ]; then
PHPUNIT_EXEC="${PHPUNIT_EXEC} --extension "${DAMA_EXTENSION}""
fi

if [ "${USE_FOUNDRY_PHPUNIT_EXTENSION:-0}" = "1" ]; then
PHPUNIT_EXEC="${PHPUNIT_EXEC} --extension "${FOUNDRY_EXTENSION}""
fi

$PHPUNIT_EXEC
;;
esac
### <<
2 changes: 1 addition & 1 deletion phpunit-10.xml.dist
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="tests/bootstrap.php"
colors="true"
failOnRisky="true"
Expand Down
19 changes: 13 additions & 6 deletions src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ final class Configuration
*/
public $instantiator;

/** @var \Closure():self|self|null */
private static \Closure|self|null $instance = null;
private bool $bootedForDataProvider = false;

private static self|null $instance = null;

/**
* @param InstantiatorCallable $instantiator
Expand Down Expand Up @@ -66,23 +67,29 @@ public function assertPersistanceEnabled(): void
}
}

public function inADataProvider(): bool
{
return $this->bootedForDataProvider;
}

public static function instance(): self
{
if (!self::$instance) {
throw new FoundryNotBooted('Foundry is not yet booted. Ensure ZenstruckFoundryBundle is enabled. If in a test, ensure your TestCase has the Factories trait.');
throw new FoundryNotBooted();
}

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

public static function isBooted(): bool
{
return null !== self::$instance;
}

public static function boot(\Closure|self $configuration): void
public static function boot(\Closure|self $configuration, bool $bootForDataProvider = false): void
{
self::$instance = $configuration;
self::$instance = \is_callable($configuration) ? ($configuration)() : $configuration;
self::$instance->bootedForDataProvider = $bootForDataProvider;
}

public static function shutdown(): void
Expand Down
4 changes: 4 additions & 0 deletions src/Exception/FoundryNotBooted.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@
*/
final class FoundryNotBooted extends \LogicException
{
public function __construct()
{
parent::__construct('Foundry is not yet booted. Ensure ZenstruckFoundryBundle is enabled. If in a test, ensure your TestCase has the Factories trait.');
}
}
43 changes: 43 additions & 0 deletions src/PHPUnit/BootFoundryOnDataProviderMethodCalled.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\PHPUnit;

use App\Infrastructure\Common\Symfony\Kernel;
use PHPUnit\Event;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpKernel\KernelInterface;
use Zenstruck\Foundry\Configuration;
use Zenstruck\Foundry\Test\UnitTestConfig;

/**
* @author Nicolas PHILIPPE <[email protected]>
*/
final class BootFoundryOnDataProviderMethodCalled implements Event\Test\DataProviderMethodCalledSubscriber
{
public function __construct(
private KernelInterface $kernel
)
{
}

public function notify(Event\Test\DataProviderMethodCalled $event): void
{
if (is_a($event->testMethod()->className(), KernelTestCase::class, allow_string: true)) {
static $kernelIsBooted = false;

if (!$kernelIsBooted) {
$this->kernel->boot();
$kernelIsBooted = true;
}

Configuration::boot(
fn() => $this->kernel->getContainer()->get('.zenstruck_foundry.configuration'),
bootForDataProvider: true
);
} else {
Configuration::boot(UnitTestConfig::build(), bootForDataProvider: true);
}
}
}
72 changes: 72 additions & 0 deletions src/PHPUnit/Extension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\PHPUnit;

use PHPUnit\Metadata\Version\ConstraintRequirement;
use PHPUnit\Runner;
use PHPUnit\TextUI;
use Symfony\Component\HttpKernel\KernelInterface;
use Zenstruck\Foundry\Configuration;

final class Extension implements Runner\Extension\Extension
{
const MIN_PHPUNIT_VERSION = '11.4';

public function bootstrap(
TextUI\Configuration\Configuration $configuration,
Runner\Extension\Facade $facade,
Runner\Extension\ParameterCollection $parameters
): void {
if (!ConstraintRequirement::from(self::MIN_PHPUNIT_VERSION)->isSatisfiedBy(Runner\Version::id())) {
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
)
);
}

// shutdown Foundry if for some reason it has been booted before
if (Configuration::isBooted()) {
Configuration::shutdown();
}

$kernel = $this->createKernel();

$facade->registerSubscribers(
new BootFoundryOnDataProviderMethodCalled($kernel),
new ShutdownKernelOnTestSuiteLoaded($kernel)
);
}

/**
* This logic was shamelessly stolen from Symfony's KernelTestCase
*/
private function createKernel(): KernelInterface
{
if (!isset($_SERVER['KERNEL_CLASS']) && !isset($_ENV['KERNEL_CLASS'])) {
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.');
}

if (!class_exists($class = $_ENV['KERNEL_CLASS'] ?? $_SERVER['KERNEL_CLASS'])) {
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
)
);
}

/**
* @var class-string<KernelInterface> $class
*/

$env = $_ENV['APP_ENV'] ?? $_SERVER['APP_ENV'] ?? 'test';
$debug = $_ENV['APP_DEBUG'] ?? $_SERVER['APP_DEBUG'] ?? true;

return new $class($env, $debug);
}
}
26 changes: 26 additions & 0 deletions src/PHPUnit/ShutdownKernelOnTestSuiteLoaded.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Zenstruck\Foundry\PHPUnit;

use PHPUnit\Event;
use Symfony\Component\HttpKernel\KernelInterface;
use Zenstruck\Foundry\Configuration;

/**
* @author Nicolas PHILIPPE <[email protected]>
*/
final class ShutdownKernelOnTestSuiteLoaded implements Event\TestSuite\LoadedSubscriber
{
public function __construct(
private KernelInterface $kernel
) {
}

public function notify(Event\TestSuite\Loaded $event): void
{
$this->kernel->shutdown();
Configuration::shutdown();
}
}
Loading

0 comments on commit a1c9816

Please sign in to comment.