Skip to content

Commit

Permalink
Allows Setup and Teardown actions to be reused in alternative TestCas…
Browse files Browse the repository at this point in the history
…e for Laravel (#49973)

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* Apply fixes from StyleCI

* formatting

---------

Signed-off-by: Mior Muhammad Zaki <[email protected]>
Co-authored-by: StyleCI Bot <[email protected]>
Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
3 people authored Feb 8, 2024
1 parent d4390fe commit 7fac93b
Show file tree
Hide file tree
Showing 2 changed files with 286 additions and 235 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
<?php

namespace Illuminate\Foundation\Testing\Concerns;

use Carbon\CarbonImmutable;
use Illuminate\Console\Application as Artisan;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bootstrap\HandleExceptions;
use Illuminate\Foundation\Console\AboutCommand;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\TrimStrings;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\WithoutEvents;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Queue\Queue;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Facades\ParallelTesting;
use Illuminate\Support\Sleep;
use Illuminate\Support\Str;
use Illuminate\View\Component;
use Mockery;
use Mockery\Exception\InvalidCountException;

trait InteractsWithTestCaseLifecycle
{
/**
* The Illuminate application instance.
*
* @var \Illuminate\Foundation\Application
*/
protected $app;

/**
* The callbacks that should be run after the application is created.
*
* @var array
*/
protected $afterApplicationCreatedCallbacks = [];

/**
* The callbacks that should be run before the application is destroyed.
*
* @var array
*/
protected $beforeApplicationDestroyedCallbacks = [];

/**
* The exception thrown while running an application destruction callback.
*
* @var \Throwable
*/
protected $callbackException;

/**
* Indicates if we have made it through the base setUp function.
*
* @var bool
*/
protected $setUpHasRun = false;

/**
* Setup the test environment.
*
* @internal
*
* @return void
*/
protected function setUpTheTestEnvironment(): void
{
Facade::clearResolvedInstances();

if (! $this->app) {
$this->refreshApplication();

ParallelTesting::callSetUpTestCaseCallbacks($this);
}

$this->setUpTraits();

foreach ($this->afterApplicationCreatedCallbacks as $callback) {
$callback();
}

Model::setEventDispatcher($this->app['events']);

$this->setUpHasRun = true;
}

/**
* Clean up the testing environment before the next test.
*
* @internal
*
* @return void
*/
protected function tearDownTheTestEnvironment(): void
{
if ($this->app) {
$this->callBeforeApplicationDestroyedCallbacks();

ParallelTesting::callTearDownTestCaseCallbacks($this);

$this->app->flush();

$this->app = null;
}

$this->setUpHasRun = false;

if (property_exists($this, 'serverVariables')) {
$this->serverVariables = [];
}

if (property_exists($this, 'defaultHeaders')) {
$this->defaultHeaders = [];
}

if (class_exists('Mockery')) {
if ($container = Mockery::getContainer()) {
$this->addToAssertionCount($container->mockery_getExpectationCount());
}

try {
Mockery::close();
} catch (InvalidCountException $e) {
if (! Str::contains($e->getMethodName(), ['doWrite', 'askQuestion'])) {
throw $e;
}
}
}

if (class_exists(Carbon::class)) {
Carbon::setTestNow();
}

if (class_exists(CarbonImmutable::class)) {
CarbonImmutable::setTestNow();
}

$this->afterApplicationCreatedCallbacks = [];
$this->beforeApplicationDestroyedCallbacks = [];

$this->originalExceptionHandler = null;
$this->originalDeprecationHandler = null;

AboutCommand::flushState();
Artisan::forgetBootstrappers();
Component::flushCache();
Component::forgetComponentsResolver();
Component::forgetFactory();
ConvertEmptyStringsToNull::flushState();
HandleExceptions::forgetApp();
Queue::createPayloadUsing(null);
Sleep::fake(false);
TrimStrings::flushState();

if ($this->callbackException) {
throw $this->callbackException;
}
}

/**
* Boot the testing helper traits.
*
* @return array
*/
protected function setUpTraits()
{
$uses = array_flip(class_uses_recursive(static::class));

if (isset($uses[RefreshDatabase::class])) {
$this->refreshDatabase();
}

if (isset($uses[DatabaseMigrations::class])) {
$this->runDatabaseMigrations();
}

if (isset($uses[DatabaseTruncation::class])) {
$this->truncateDatabaseTables();
}

if (isset($uses[DatabaseTransactions::class])) {
$this->beginDatabaseTransaction();
}

if (isset($uses[WithoutMiddleware::class])) {
$this->disableMiddlewareForAllTests();
}

if (isset($uses[WithoutEvents::class])) {
$this->disableEventsForAllTests();
}

if (isset($uses[WithFaker::class])) {
$this->setUpFaker();
}

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;
}

/**
* Clean up the testing environment before the next test case.
*
* @internal
*
* @return void
*/
public static function tearDownAfterClassUsingTestCase()
{
foreach ([
\PHPUnit\Util\Annotation\Registry::class,
\PHPUnit\Metadata\Annotation\Parser\Registry::class,
] as $class) {
if (class_exists($class)) {
(function () {
$this->classDocBlocks = [];
$this->methodDocBlocks = [];
})->call($class::getInstance());
}
}
}

/**
* Register a callback to be run after the application is created.
*
* @param callable $callback
* @return void
*/
public function afterApplicationCreated(callable $callback)
{
$this->afterApplicationCreatedCallbacks[] = $callback;

if ($this->setUpHasRun) {
$callback();
}
}

/**
* Register a callback to be run before the application is destroyed.
*
* @param callable $callback
* @return void
*/
protected function beforeApplicationDestroyed(callable $callback)
{
$this->beforeApplicationDestroyedCallbacks[] = $callback;
}

/**
* Execute the application's pre-destruction callbacks.
*
* @return void
*/
protected function callBeforeApplicationDestroyedCallbacks()
{
foreach ($this->beforeApplicationDestroyedCallbacks as $callback) {
try {
$callback();
} catch (Throwable $e) {
if (! $this->callbackException) {
$this->callbackException = $e;
}
}
}
}
}
Loading

0 comments on commit 7fac93b

Please sign in to comment.