Your IP : 3.135.220.239
<?php
namespace Illuminate\Foundation\Testing;
use Carbon\CarbonImmutable;
use Illuminate\Console\Application as Artisan;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bootstrap\HandleExceptions;
use Illuminate\Http\RedirectResponse;
use Illuminate\Queue\Queue;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Facades\ParallelTesting;
use Illuminate\Support\Str;
use Illuminate\Testing\AssertableJsonString;
use Mockery;
use Mockery\Exception\InvalidCountException;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\TestCase as BaseTestCase;
use PHPUnit\Util\Annotation\Registry;
use ReflectionProperty;
use Throwable;
abstract class TestCase extends BaseTestCase
{
use Concerns\InteractsWithContainer,
Concerns\MakesHttpRequests,
Concerns\InteractsWithAuthentication,
Concerns\InteractsWithConsole,
Concerns\InteractsWithDatabase,
Concerns\InteractsWithDeprecationHandling,
Concerns\InteractsWithExceptionHandling,
Concerns\InteractsWithSession,
Concerns\InteractsWithTime,
Concerns\InteractsWithViews,
Concerns\MocksApplicationServices;
/**
* 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;
/**
* Creates the application.
*
* Needs to be implemented by subclasses.
*
* @return \Symfony\Component\HttpKernel\HttpKernelInterface
*/
abstract public function createApplication();
/**
* Setup the test environment.
*
* @return void
*/
protected function setUp(): void
{
static::$latestResponse = null;
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;
}
/**
* Refresh the application instance.
*
* @return void
*/
protected function refreshApplication()
{
$this->app = $this->createApplication();
}
/**
* 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[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.
*
* @return void
*
* @throws \Mockery\Exception\InvalidCountException
*/
protected function tearDown(): 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;
Artisan::forgetBootstrappers();
Queue::createPayloadUsing(null);
HandleExceptions::forgetApp();
if ($this->callbackException) {
throw $this->callbackException;
}
}
/**
* Clean up the testing environment before the next test case.
*
* @return void
*/
public static function tearDownAfterClass(): void
{
static::$latestResponse = null;
(function () {
$this->classDocBlocks = [];
$this->methodDocBlocks = [];
})->call(Registry::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;
}
}
}
}
/**
* This method is called when a test method did not execute successfully.
*
* @param \Throwable $exception
* @return void
*/
protected function onNotSuccessfulTest(Throwable $exception): void
{
if (! $exception instanceof ExpectationFailedException || is_null(static::$latestResponse)) {
parent::onNotSuccessfulTest($exception);
}
if ($lastException = static::$latestResponse->exceptions->last()) {
parent::onNotSuccessfulTest($this->appendExceptionToException($lastException, $exception));
return;
}
if (static::$latestResponse->baseResponse instanceof RedirectResponse) {
$session = static::$latestResponse->baseResponse->getSession();
if (! is_null($session) && $session->has('errors')) {
parent::onNotSuccessfulTest($this->appendErrorsToException($session->get('errors')->all(), $exception));
return;
}
}
if (static::$latestResponse->baseResponse->headers->get('Content-Type') === 'application/json') {
$testJson = new AssertableJsonString(static::$latestResponse->getContent());
if (isset($testJson['errors'])) {
parent::onNotSuccessfulTest($this->appendErrorsToException($testJson->json(), $exception, true));
return;
}
}
parent::onNotSuccessfulTest($exception);
}
/**
* Append an exception to the message of another exception.
*
* @param \Throwable $exceptionToAppend
* @param \Throwable $exception
* @return \Throwable
*/
protected function appendExceptionToException($exceptionToAppend, $exception)
{
$exceptionMessage = $exceptionToAppend->getMessage();
$exceptionToAppend = (string) $exceptionToAppend;
$message = <<<"EOF"
The following exception occurred during the last request:
$exceptionToAppend
----------------------------------------------------------------------------------
$exceptionMessage
EOF;
return $this->appendMessageToException($message, $exception);
}
/**
* Append errors to an exception message.
*
* @param array $errors
* @param \Throwable $exception
* @param bool $json
* @return \Throwable
*/
protected function appendErrorsToException($errors, $exception, $json = false)
{
$errors = $json
? json_encode($errors, JSON_PRETTY_PRINT)
: implode(PHP_EOL, Arr::flatten($errors));
// JSON error messages may already contain the errors, so we shouldn't duplicate them...
if (str_contains($exception->getMessage(), $errors)) {
return $exception;
}
$message = <<<"EOF"
The following errors occurred during the last request:
$errors
EOF;
return $this->appendMessageToException($message, $exception);
}
/**
* Append a message to an exception.
*
* @param string $message
* @param \Throwable $exception
* @return \Throwable
*/
protected function appendMessageToException($message, $exception)
{
$property = new ReflectionProperty($exception, 'message');
$property->setAccessible(true);
$property->setValue(
$exception,
$exception->getMessage().PHP_EOL.PHP_EOL.$message.PHP_EOL
);
return $exception;
}
}