TestCase.php
TLDR
This file defines the TestCase
class, which is an abstract class that extends the BaseTestCase
class. It contains several properties and methods that are used in testing Laravel applications. The TestCase
class also implements various traits that provide additional functionality for testing.
Methods
createApplication
Creates the application instance. This method needs to be implemented by subclasses.
setUp
Sets up the test environment. This method is called before each test method is run.
refreshApplication
Refreshes the application instance.
setUpTraits
Bootstraps the testing helper traits.
transformException
Transforms a thrown exception before it is thrown again.
tearDown
Cleans up the testing environment after each test method is run.
tearDownAfterClass
Cleans up the testing environment after all test methods in the class have been run.
afterApplicationCreated
Registers a callback to be run after the application is created.
beforeApplicationDestroyed
Registers a callback to be run before the application is destroyed.
callBeforeApplicationDestroyedCallbacks
Executes the application's pre-destruction callbacks.
Classes
None
<?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\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\TrimStrings;
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;
use PHPUnit\Framework\TestCase as BaseTestCase;
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;
/**
* 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[DatabaseTruncation::class])) {
$this->truncateDatabaseTables();
}
if (isset($uses[DatabaseTransactions::class])) {
$this->beginDatabaseTransaction();
}
if (isset($uses[WithoutMiddleware::class])) {
$this->disableMiddlewareForAllTests();
}
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;
}
/**
* {@inheritdoc}
*/
protected function transformException(Throwable $error): Throwable
{
$response = static::$latestResponse ?? null;
if (! is_null($response)) {
$response->transformNotSuccessfulException($error);
}
return $error;
}
/**
* 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();
Component::flushCache();
Component::forgetComponentsResolver();
Component::forgetFactory();
ConvertEmptyStringsToNull::flushState();
HandleExceptions::forgetApp();
Queue::createPayloadUsing(null);
Sleep::fake(false);
TrimStrings::flushState();
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;
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;
}
}
}
}
}