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 Oct 17, 2024
1 parent 0f72ea5 commit d31b282
Show file tree
Hide file tree
Showing 33 changed files with 889 additions and 180 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"
25 changes: 23 additions & 2 deletions .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 Expand Up @@ -155,7 +175,8 @@ jobs:
DATABASE_URL: postgresql://root:root@localhost:5432/foundry?serverVersion=15
MONGO_URL: mongodb://127.0.0.1:27017/dbName?compressors=disabled&gssapiServiceName=mongodb
USE_DAMA_DOCTRINE_TEST_BUNDLE: 1
PHPUNIT_VERSION: 9
USE_FOUNDRY_PHPUNIT_EXTENSION: 1
PHPUNIT_VERSION: 11.4
services:
mongo:
image: mongo:4
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 versions (this method changed in PHPUnit 10)
- 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\Test\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
24 changes: 20 additions & 4 deletions src/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ final class Configuration
*/
public $instantiator;

/** @var \Closure():self|self|null */
private static \Closure|self|null $instance = null;
/**
* This property is only filled if the PHPUnit extension is used!
*/
private bool $bootedForDataProvider = false;

private static ?self $instance = null;

/**
* @param InstantiatorCallable $instantiator
Expand Down Expand Up @@ -66,10 +70,15 @@ 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;
Expand All @@ -82,7 +91,14 @@ public static function isBooted(): bool

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

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

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.');
}
}
6 changes: 5 additions & 1 deletion src/Persistence/IsProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

namespace Zenstruck\Foundry\Persistence;

use Doctrine\ODM\MongoDB\DocumentManager;
use Symfony\Component\VarExporter\LazyProxyTrait;
use Zenstruck\Assert;
use Zenstruck\Foundry\Configuration;
Expand Down Expand Up @@ -128,6 +127,11 @@ public function _assertNotPersisted(string $message = '{entity} is persisted but
return $this;
}

public function _initializeLazyObject(): void
{
$this->initializeLazyObject();
}

private function isPersisted(): bool
{
try {
Expand Down
1 change: 0 additions & 1 deletion src/Persistence/PersistenceStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

namespace Zenstruck\Foundry\Persistence;

use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\Mapping\ClassMetadata;
use Doctrine\Persistence\Mapping\MappingException;
Expand Down
27 changes: 26 additions & 1 deletion src/Persistence/PersistentObjectFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,15 @@ final public static function truncate(): void
}

/**
* @final
* @return T
*/
final public function create(callable|array $attributes = []): object
public function create(callable|array $attributes = []): object
{
$object = parent::create($attributes);

$this->throwIfCannotCreateObject();

if (!$this->isPersisting()) {
return $this->proxy($object);
}
Expand Down Expand Up @@ -361,4 +364,26 @@ private function proxy(object $object): object

return $this->isPersisting() ? $object : $object->_disableAutoRefresh();
}

private function throwIfCannotCreateObject(): void
{
$configuration = Configuration::instance();

/**
* "false === $configuration->inADataProvider()" would also mean that the PHPUnit extension is NOT used
* so a `FoundryNotBooted` exception would be thrown if we actually are in a data provider.
*/
if (!$configuration->inADataProvider()) {
return;
}

if (
!$configuration->isPersistenceAvailable()
|| $this instanceof PersistentProxyObjectFactory
) {
return;
}

throw new \LogicException(\sprintf('Cannot create object in a data provider for non-proxy factories. Transform your factory into a "%s", or call "create()" method in the test. See https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#phpunit-data-providers', PersistentProxyObjectFactory::class));
}
}
15 changes: 14 additions & 1 deletion src/Persistence/PersistentProxyObjectFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
use Doctrine\Persistence\ObjectRepository;
use Zenstruck\Foundry\Configuration;
use Zenstruck\Foundry\Factory;
use Zenstruck\Foundry\Object\Instantiator;
use Zenstruck\Foundry\FactoryCollection; // keep me!
use Zenstruck\Foundry\Object\Instantiator;

/**
* @author Kevin Bond <[email protected]>
Expand Down Expand Up @@ -43,6 +43,19 @@
*/
abstract class PersistentProxyObjectFactory extends PersistentObjectFactory
{
/**
* @return T&Proxy<T>
*/
final public function create(callable|array $attributes = []): object
{
$configuration = Configuration::instance();
if ($configuration->inADataProvider()) {
return ProxyGenerator::wrapFactory($this, $attributes);
}

return parent::create($attributes);
}

/**
* @return class-string<T>
*/
Expand Down
5 changes: 5 additions & 0 deletions src/Persistence/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,9 @@ public function _assertNotPersisted(string $message = '{entity} is persisted but
* @return ProxyRepositoryDecorator<T,ObjectRepository<T>>
*/
public function _repository(): ProxyRepositoryDecorator;

/**
* @internal
*/
public function _initializeLazyObject(): void;
}
Loading

0 comments on commit d31b282

Please sign in to comment.