diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md index 2322b498b..d26809581 100644 --- a/CHANGELOG-3.1.md +++ b/CHANGELOG-3.1.md @@ -24,6 +24,7 @@ - [#5815](https://github.com/hyperf/hyperf/pull/5815) Added alias as `mysql` for `pdo` in `hyperf/db`. - [#5849](https://github.com/hyperf/hyperf/pull/5849) Support for insert update and select using enums. - [#5855](https://github.com/hyperf/hyperf/pull/5855) Added `hyperf/closure-command` component. +- [#5894](https://github.com/hyperf/hyperf/pull/5894) Added `model-factory` support for `hyperf/testing`. ## Optimized diff --git a/composer.json b/composer.json index fec6c7454..ac8f2d7b1 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "doctrine/instantiator": "^1.0", "egulias/email-validator": "^3.0", "elasticsearch/elasticsearch": "^7.0", + "fakerphp/faker": "^1.23", "fig/http-message-util": "^1.1.2", "filp/whoops": "^2.7", "friendsofphp/php-cs-fixer": "~3.16.0", diff --git a/src/testing/composer.json b/src/testing/composer.json index 7ba656e84..fb30a537c 100644 --- a/src/testing/composer.json +++ b/src/testing/composer.json @@ -32,6 +32,9 @@ "hyperf/utils": "~3.1.0", "symfony/http-foundation": "^5.4|^6.0" }, + "suggest": { + "fakerphp/faker": "Required to use Faker feature.(^1.23)" + }, "extra": { "branch-alias": { "dev-master": "3.0-dev" diff --git a/src/testing/src/Concerns/InteractsWithModelFactory.php b/src/testing/src/Concerns/InteractsWithModelFactory.php new file mode 100644 index 000000000..8eabb63aa --- /dev/null +++ b/src/testing/src/Concerns/InteractsWithModelFactory.php @@ -0,0 +1,44 @@ +modelFactory = ModelFactory::create( + FakerFactory::create('en_US') + ); + + foreach ((array) $this->factoryPath as $path) { + if (is_dir($path)) { + $this->modelFactory->load($path); + } + } + } + + protected function tearDownInteractsWithModelFactory() + { + $this->modelFactory = null; + } +} diff --git a/src/testing/src/ModelFactory.php b/src/testing/src/ModelFactory.php new file mode 100644 index 000000000..1b7da6d67 --- /dev/null +++ b/src/testing/src/ModelFactory.php @@ -0,0 +1,53 @@ + $model + * @return T + */ + public function factory(string $model, ...$arguments) + { + $factory = $this->factory; + + if (isset($arguments[1]) && is_string($arguments[1])) { + return $factory->of($arguments[0], $arguments[1])->times($arguments[2] ?? null); + } + if (isset($arguments[1])) { + return $factory->of($arguments[0])->times($arguments[1]); + } + + return $factory->of($arguments[0]); + } + + public function load(string $path): void + { + $this->factory->load($path); + } +} diff --git a/src/testing/src/TestCase.php b/src/testing/src/TestCase.php index 36cc4d8ec..96e0d1601 100644 --- a/src/testing/src/TestCase.php +++ b/src/testing/src/TestCase.php @@ -14,6 +14,9 @@ namespace Hyperf\Testing; use Mockery as m; use Throwable; +use function Hyperf\Support\class_basename; +use function Hyperf\Support\class_uses_recursive; + /** * @internal * @coversNothing @@ -21,21 +24,95 @@ use Throwable; abstract class TestCase extends \PHPUnit\Framework\TestCase { use Concerns\InteractsWithContainer; + use Concerns\InteractsWithModelFactory; use Concerns\MakesHttpRequests; use Concerns\RunTestsInCoroutine; + /** + * The callbacks that should be run after the application is created. + */ + protected array $afterApplicationCreatedCallbacks = []; + + /** + * The callbacks that should be run before the application is destroyed. + */ + protected array $beforeApplicationDestroyedCallbacks = []; + + /** + * The exception thrown while running an application destruction callback. + */ + protected ?Throwable $callbackException = null; + protected function setUp(): void { $this->refreshContainer(); + + $this->setUpTraits(); + + foreach ($this->afterApplicationCreatedCallbacks as $callback) { + $callback(); + } } protected function tearDown(): void { $this->flushContainer(); + $this->callBeforeApplicationDestroyedCallbacks(); + try { m::close(); - } catch (Throwable) { + } catch (Throwable $e) { + } + + if ($this->callbackException) { + throw $this->callbackException; + } + } + + /** + * Boot the testing helper traits. + * + * @return array + */ + protected function setUpTraits() + { + $uses = array_flip(class_uses_recursive(static::class)); + + foreach ($uses as $trait) { + if (method_exists($this, $method = 'setUp' . class_basename($trait))) { + $this->{$method}(); + } + + if (method_exists($this, $method = 'tearDown' . class_basename($trait))) { + $this->beforeApplicationDestroyed(fn () => $this->{$method}()); + } + } + + return $uses; + } + + /** + * Register a callback to be run before the application is destroyed. + */ + protected function beforeApplicationDestroyed(callable $callback) + { + $this->beforeApplicationDestroyedCallbacks[] = $callback; + } + + /** + * Execute the application's pre-destruction callbacks. + */ + protected function callBeforeApplicationDestroyedCallbacks() + { + foreach ($this->beforeApplicationDestroyedCallbacks as $callback) { + try { + $callback(); + } catch (Throwable $e) { + if (! $this->callbackException) { + $this->callbackException = $e; + } + } } } }