diff --git a/src/Concerns/HandlesDatabases.php b/src/Concerns/HandlesDatabases.php index 726f64aa..9c041eeb 100644 --- a/src/Concerns/HandlesDatabases.php +++ b/src/Concerns/HandlesDatabases.php @@ -7,9 +7,10 @@ use Orchestra\Testbench\Attributes\DefineDatabase; use Orchestra\Testbench\Attributes\RequiresDatabase; use Orchestra\Testbench\Attributes\WithMigration; -use Orchestra\Testbench\Exceptions\ApplicationNotAvailableException; use Orchestra\Testbench\Features\TestingFeature; +use function Orchestra\Testbench\laravel_or_fail; + /** * @internal */ @@ -24,9 +25,7 @@ trait HandlesDatabases */ protected function setUpDatabaseRequirements(Closure $callback): void { - if (\is_null($app = $this->app)) { - throw ApplicationNotAvailableException::make(__METHOD__); - } + $app = laravel_or_fail($this->app); TestingFeature::run( testCase: $this, @@ -81,9 +80,7 @@ protected function setUpDatabaseRequirements(Closure $callback): void */ protected function usesSqliteInMemoryDatabaseConnection(?string $connection = null): bool { - if (\is_null($app = $this->app)) { - throw ApplicationNotAvailableException::make(__METHOD__); - } + $app = laravel_or_fail($this->app); /** @var \Illuminate\Contracts\Config\Repository $config */ $config = $app->make('config'); diff --git a/src/Concerns/InteractsWithMigrations.php b/src/Concerns/InteractsWithMigrations.php index 881f3052..ab6d4937 100644 --- a/src/Concerns/InteractsWithMigrations.php +++ b/src/Concerns/InteractsWithMigrations.php @@ -7,9 +7,9 @@ use InvalidArgumentException; use Orchestra\Testbench\Attributes\ResetRefreshDatabaseState; use Orchestra\Testbench\Database\MigrateProcessor; -use Orchestra\Testbench\Exceptions\ApplicationNotAvailableException; use function Orchestra\Testbench\default_migration_path; +use function Orchestra\Testbench\laravel_or_fail; use function Orchestra\Testbench\load_migration_paths; /** @@ -67,18 +67,17 @@ protected function tearDownInteractsWithMigrations(): void */ protected function loadMigrationsFrom(array|string $paths): void { + $app = laravel_or_fail($this->app); + if ( (\is_string($paths) || Arr::isList($paths)) && static::usesRefreshDatabaseTestingConcern() && RefreshDatabaseState::$migrated === false && RefreshDatabaseState::$lazilyRefreshed === false ) { - if (\is_null($this->app)) { - throw ApplicationNotAvailableException::make(__METHOD__); - } /** @var array|string $paths */ - load_migration_paths($this->app, $paths); + load_migration_paths($app, $paths); return; } @@ -109,16 +108,14 @@ protected function loadMigrationsFrom(array|string $paths): void #[\Deprecated(message: 'Use `loadMigrationsFrom()` instead', since: '9.0.7')] protected function loadMigrationsWithoutRollbackFrom(array|string $paths): void { - if (\is_null($this->app)) { - throw ApplicationNotAvailableException::make(__METHOD__); - } + $app = laravel_or_fail($this->app); $migrator = new MigrateProcessor($this, $this->resolvePackageMigrationsOptions($paths)); $migrator->up(); array_unshift($this->cachedTestMigratorProcessors, $migrator); - $this->resetApplicationArtisanCommands($this->app); + $this->resetApplicationArtisanCommands($app); } /** @@ -154,9 +151,7 @@ protected function resolvePackageMigrationsOptions(array|string $paths = []): ar */ protected function loadLaravelMigrations(array|string $database = []): void { - if (\is_null($this->app)) { - throw ApplicationNotAvailableException::make(__METHOD__); - } + $app = laravel_or_fail($this->app); $options = $this->resolveLaravelMigrationsOptions($database); $options['--path'] = default_migration_path(); @@ -167,7 +162,7 @@ protected function loadLaravelMigrations(array|string $database = []): void array_unshift($this->cachedTestMigratorProcessors, $migrator); - $this->resetApplicationArtisanCommands($this->app); + $this->resetApplicationArtisanCommands($app); } /** @@ -196,16 +191,14 @@ protected function loadLaravelMigrationsWithoutRollback(array|string $database = */ protected function runLaravelMigrations(array|string $database = []): void { - if (\is_null($this->app)) { - throw ApplicationNotAvailableException::make(__METHOD__); - } + $app = laravel_or_fail($this->app); $migrator = new MigrateProcessor($this, $this->resolveLaravelMigrationsOptions($database)); $migrator->up(); array_unshift($this->cachedTestMigratorProcessors, $migrator); - $this->resetApplicationArtisanCommands($this->app); + $this->resetApplicationArtisanCommands($app); } /** diff --git a/src/Concerns/InteractsWithTestCase.php b/src/Concerns/InteractsWithTestCase.php index 491807e3..f199adb1 100644 --- a/src/Concerns/InteractsWithTestCase.php +++ b/src/Concerns/InteractsWithTestCase.php @@ -10,9 +10,10 @@ use Orchestra\Testbench\Contracts\Attributes\BeforeAll as BeforeAllContract; use Orchestra\Testbench\Contracts\Attributes\BeforeEach as BeforeEachContract; use Orchestra\Testbench\Contracts\Attributes\Resolvable as ResolvableContract; -use Orchestra\Testbench\Exceptions\ApplicationNotAvailableException; use Orchestra\Testbench\PHPUnit\AttributeParser; +use function Orchestra\Testbench\laravel_or_fail; + /** * @phpstan-import-type TTestingFeature from \Orchestra\Testbench\PHPUnit\AttributeParser * @phpstan-import-type TAttributes from \Orchestra\Testbench\PHPUnit\AttributeParser @@ -137,10 +138,7 @@ abstract protected static function resolvePhpUnitAttributesForMethod(string $cla */ protected function setUpTheTestEnvironmentUsingTestCase(): void { - /** @phpstan-ignore-next-line */ - if (\is_null($app = $this->app)) { - throw ApplicationNotAvailableException::make(__METHOD__); - } + $app = laravel_or_fail($this->app); $this->resolvePhpUnitAttributes() ->flatten() @@ -159,10 +157,7 @@ protected function setUpTheTestEnvironmentUsingTestCase(): void */ protected function tearDownTheTestEnvironmentUsingTestCase(): void { - /** @phpstan-ignore-next-line */ - if (\is_null($app = $this->app)) { - throw ApplicationNotAvailableException::make(__METHOD__); - } + $app = laravel_or_fail($this->app); $this->resolvePhpUnitAttributes() ->flatten() diff --git a/src/Concerns/InteractsWithWorkbench.php b/src/Concerns/InteractsWithWorkbench.php index 2841f15e..851e9994 100644 --- a/src/Concerns/InteractsWithWorkbench.php +++ b/src/Concerns/InteractsWithWorkbench.php @@ -73,7 +73,7 @@ protected function getPackageBootstrappersUsingWorkbench($app): ?array * @internal * * @param \Illuminate\Foundation\Application $app - * @return array|null + * @return array>|null */ protected function getPackageProvidersUsingWorkbench($app): ?array { diff --git a/src/Concerns/WithFactories.php b/src/Concerns/WithFactories.php index 22642929..19368b1a 100644 --- a/src/Concerns/WithFactories.php +++ b/src/Concerns/WithFactories.php @@ -4,7 +4,8 @@ use Exception; use Illuminate\Database\Eloquent\Factory as ModelFactory; -use Orchestra\Testbench\Exceptions\ApplicationNotAvailableException; + +use function Orchestra\Testbench\laravel_or_fail; /** * @api @@ -27,11 +28,7 @@ trait WithFactories */ protected function withFactories(string $path) { - if (\is_null($this->app)) { - throw ApplicationNotAvailableException::make(__METHOD__); - } - - return $this->loadFactoriesUsing($this->app, $path); + return $this->loadFactoriesUsing(laravel_or_fail($this->app), $path); } /** diff --git a/src/Foundation/Application.php b/src/Foundation/Application.php index eb3c59be..f8255450 100644 --- a/src/Foundation/Application.php +++ b/src/Foundation/Application.php @@ -47,6 +47,13 @@ class Application resolveApplicationConfiguration as protected resolveApplicationConfigurationFromTrait; } + /** + * The Illuminate application instance. + * + * @var \Illuminate\Foundation\Application|null + */ + protected $app; + /** * List of configurations. * diff --git a/src/functions.php b/src/functions.php index 6e25ede7..fc475151 100644 --- a/src/functions.php +++ b/src/functions.php @@ -441,3 +441,34 @@ function join_paths(?string $basePath, string ...$paths): string return $basePath.implode('', $paths); } + +/** + * Ensure the provided `$app` return an instance of Laravel application or throw an exception. + * + * @internal + * + * @param \Illuminate\Foundation\Application|null $app + * @param string|null $caller + * @return \Illuminate\Foundation\Application + * + * @throws \Orchestra\Testbench\Exceptions\ApplicationNotAvailableException + */ +function laravel_or_fail($app, ?string $caller = null): Application +{ + if ($app instanceof Application) { + return $app; + } + + if (\is_null($caller)) { + $caller = transform(debug_backtrace()[1], function ($debug) { + /** @phpstan-ignore isset.offset */ + if (isset($debug['class']) && isset($debug['function'])) { + return \sprintf('%s::%s', $debug['class'], $debug['function']); + } + + return $debug['function']; + }); + } + + throw Exceptions\ApplicationNotAvailableException::make($caller); +} diff --git a/tests/HelpersTest.php b/tests/HelpersTest.php index 267ba062..4086be24 100644 --- a/tests/HelpersTest.php +++ b/tests/HelpersTest.php @@ -3,10 +3,12 @@ namespace Orchestra\Testbench\Tests; use Illuminate\Foundation\Application; +use Orchestra\Testbench\Exceptions\ApplicationNotAvailableException; use Orchestra\Testbench\TestCase; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Runner\Version; +use function Orchestra\Testbench\laravel_or_fail; use function Orchestra\Testbench\laravel_version_compare; use function Orchestra\Testbench\phpunit_version_compare; @@ -27,4 +29,13 @@ public function it_can_compare_phpunit_version() $this->assertSame(0, phpunit_version_compare(Version::id())); $this->assertTrue(phpunit_version_compare(Version::id(), '==')); } + + #[Test] + public function it_can_throw_application_not_available_application_when_app_is_not_laravel() + { + $this->expectException(ApplicationNotAvailableException::class); + $this->expectExceptionMessage(\sprintf('Application is not available to run [%s]', __METHOD__)); + + laravel_or_fail(null); + } }