When we are calling the $this->app->flush()
method after the tests in the tearDown()
method, it seems like the application isn't refreshing correctly and isn't setting all the aliases. This holds true for any class we will want to resolve out of the IoC Container which should be aliased automatically.
Edit 3 is offering a temporary solution for this issue. There is a need to find a permanent one tho.
I have some tests which use the factory()
handy method. The issue is that when I use it it errors "Class config does not exist".
/** @test */
public function it_should_login_a_confirmed_user()
{
$user = $this->createPredictableRegularUser();
$this->post('authenticate', [
'email' => '[email protected]',
'password' => 'password'
])
->seeStatusCode(200)
->seeJson(['user_id' => $user->id]);
}
where createPredictableRegularUser
is (defined in the TestCase class itself):
protected function createPredictableRegularUser($overrides = [])
{
$overrides = array_merge([
'type' => 'regular',
'email' => '[email protected]'
], $overrides);
return factory(User::class)->create($overrides);
}
When it's trying to save the entity, it tries to figure out what my db connection is so it "asks" the config class for that and it returns that the config class does not exist
.
I have some other tests (in the same file) which are successful and they do use the DB through repositories:
/** @test */
public function it_should_show_appropriate_validation_errors_upon_unsuccessful_registration_attempt()
{
$data = $this->craftRegistrationUser(['password' => '', 'email' => 'bah']);
$this->post('users', $data)
->seeStatusCode(422)
->seeJsonEquals(['validation_errors' => ['error 1', 'error 2']);
}
When I post to users
it will use UserRepository
and will indeed save the user (not in this particular test but in other tests that I haven't posted here).
Any idea why it's happening? it's super weird..
I was investigating it a little bit, and I found something rather interesting. In my test file I have these tests:
/** @test */
public function it_should_successfully_register_a_user_and_confirm_him() {//code}
/** @test */
public function it_should_show_appropriate_validation_errors_upon_unsuccessful_registration_attempt(){//code}
/** @test */
public function it_should_fail_to_confirm_a_non_existing_confirmation_token(){//code}
/** @test */
public function it_should_login_a_confirmed_user(){//code}
/** @test */
public function it_should_not_login_a_user_with_wrong_credentials(){//code}
/** @test */
public function it_should_not_login_a_not_confirmed_user(){//code}
after the first test that is trying to use the config is executed, all of the other tests that need to access the config somehow will fail with Class config does not exist
. Basically it means that if I change the order of the tests then now the tests which erorred with Class config does not exist
are passing the the others which have passed before are not failing with that error message.
I thought that maybe the application isn't being properly restarted but when I checked this by simply adding a var_dump
to the setUp
method, it showed that every single time it works:
public function setUp()
{
if (!$this->app) {
var_dump('refreshing');
$this->refreshApplication();
}
}
It seems like it doesn't matter whether I use the factory()
method or User::Create()
manually. They both are trying to access the config
key in the Application at Illuminate/Database/DatabaseManager.php:251 and fails there and says that config class does not exist.
The error stack is:
.../vendor/laravel/framework/src/Illuminate/Container/Container.php:
.../vendor/laravel/framework/src/Illuminate/Container/Container.php:626
...vendor/laravel/framework/src/Illuminate/Foundation/Application.php:674
.../vendor/laravel/framework/src/Illuminate/Container/Container.php:1157
.../vendor/laravel/framework/src/Illuminate/Database/DatabaseManager.php:251
// there are more down here but they are pretty much irrelevant
It seems like when the make
method is calling the getAlias()
method and trying to get the alias for config
it returns config
and not the concrete implementation of the config? It got me thinking that maybe it wasn't aliased properly but when I checked it, it was indeed aliased.
I've tried dd()
the $app['config']
in one of the tests that's erroring "Class config does not exist" before doing anything else in the test:
/** @test */
public function it_should_login_a_confirmed_user()
{
dd($this->app['config']);
$user = $this->createPredictableRegularUser();
$this->post('authenticate', [
'email' => '[email protected]',
'password' => 'password'
])
->seeStatusCode(200)
->seeJson(['user_id' => (string)$user->id]);
}
and it returns the config repository: Illuminate\Config\Repository
.
That's weird.. it's like when it's trying to use Eloquent it completely forgets about the config.. I've also done this:
/** @test */
public function it_should_login_a_confirmed_user()
{
var_dump($this->app['config']);
$user = $this->createPredictableRegularUser();
$this->post('authenticate', [
'email' => '[email protected]',
'password' => 'password'
])
->seeStatusCode(200)
->seeJson(['user_id' => (string)$user->id]);
}
so it won't stop the tests and see if it's still going to error that the config class does not exists even tho the config class was resolved before the test has ran successfully. Unfortunately, it kept saying the the config class does not exist.
It seems like that if I don't flush the application before each test I do not get the Class config does not exist
:
// in OUR tests/TestCase.php - override the tearDown method:
public function tearDown()
{
if (class_exists('Mockery')) {
Mockery::close();
}
if ($this->app) {
foreach ($this->beforeApplicationDestroyedCallbacks as $callback) {
call_user_func($callback);
}
// the culprit:
// $this->app->flush();
$this->app = null;
}
}
Now my tests are passing as expected. I'm kinda worried that the fact that I don't flush()
the application before a new test is going to cause weird bugs.
Please ask on the fourms.
All you have to do in your custom tearDown() after you nullify your things call parent::tearDown() that will destruct the rest of things...
This solved me the problem in Laravel 5.0.33
@xbugster Calling the parent::tearDown() is what is _causing this issue_ and not helping it!
@GrahamCampbell Would you mind opening this again:
Getting exactly the same issue - here's a little more info.
My tests have a setup and teardown method
public function setUp()
{
parent::setUp();
//Small couple of lines of my code.
}
public function tearDown()
{
parent::tearDown();
}
When my tests run, even though they all pass, I'm left with this as an error message at the end:
PHPUnit 5.0.10 by Sebastian Bergmann and contributors.
Time: 6.36 seconds, Memory: 24.00Mb
OK (2 tests, 2 assertions)
Fatal error: Uncaught ReflectionException: Class config does not exist in /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Container/Container.php on line 749
Call Stack:
0.0663 788384 1. {main}() /home/vagrant/Code/SourcePhars/phpunit.phar:0
0.8682 7918344 2. PHPUnit_TextUI_Command::main() /home/vagrant/Code/SourcePhars/phpunit.phar:513
0.8682 7918456 3. PHPUnit_TextUI_Command->run() phar:///home/vagrant/Code/SourcePhars/phpunit.phar/phpunit/TextUI/Command.php:105
ReflectionException: Class config does not exist in /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Container/Container.php on line 749
Call Stack:
0.0663 788384 1. {main}() /home/vagrant/Code/SourcePhars/phpunit.phar:0
0.8682 7918344 2. PHPUnit_TextUI_Command::main() /home/vagrant/Code/SourcePhars/phpunit.phar:513
0.8682 7918456 3. PHPUnit_TextUI_Command->run() phar:///home/vagrant/Code/SourcePhars/phpunit.phar/phpunit/TextUI/Command.php:105
6.3677 22266048 4. App\Service\HeatTimer\HeatTimer->__destruct() /home/vagrant/Code/demo.local.test/app/Service/HeatTimer/HeatTimer.php:0
6.3677 22266048 5. App\Service\HeatTimer\HeatTimer->logout() /home/vagrant/Code/demo.local.test/app/Service/HeatTimer/HeatTimer.php:276
6.3677 22266048 6. config() /home/vagrant/Code/demo.local.test/app/Service/HeatTimer/HeatTimer.php:287
6.3677 22266048 7. app() /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php:263
6.3677 22266104 8. Illuminate\Foundation\Application->make() /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php:106
6.3677 22266104 9. Illuminate\Container\Container->make() /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Foundation/Application.php:709
6.3678 22266104 10. Illuminate\Container\Container->build() /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Container/Container.php:644
6.3678 22266216 11. ReflectionClass->__construct() /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Container/Container.php:749
PHP Fatal error: Uncaught ReflectionException: Class config does not exist in /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Container/Container.php:749
Stack trace:
#0 /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Container/Container.php(749): ReflectionClass->__construct('config')
#1 /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Container/Container.php(644): Illuminate\Container\Container->build('config', Array)
#2 /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(709): Illuminate\Container\Container->make('config', Array)
#3 /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php(106): Illuminate\Foundation\Application->make('config', Array)
#4 /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php(263): app('config')
#5 /home/vagrant/Code/demo.local.test/app/Service/HeatTimer/HeatTimer.php(287): config( in /home/vagrant/Code/demo.local.test/vendor/laravel/framework/src/Illuminate/Container/Container.php on line 749
PHP Stack trace:
PHP 1. {main}() /home/vagrant/Code/SourcePhars/phpunit.phar:0
PHP 2. PHPUnit_TextUI_Command::main() /home/vagrant/Code/SourcePhars/phpunit.phar:513
PHP 3. PHPUnit_TextUI_Command->run() phar:///home/vagrant/Code/SourcePhars/phpunit.phar/phpunit/TextUI/Command.php:105
Process finished with exit code 255
After some digging around the tearDown
method, I went up the tree and finally found the line that was causing the issue.
A normal test calls parent::tearDown
which is \Illuminate\Foundation\Testing\TestCase.php
This calls $this->app->flush();
which is in \Illuminate\Foundation\Application.php
Which calls it's parent parent::flush
which is in Illuminate\Container\Container.php
Which finally runs this code:
/**
* Flush the container of all bindings and resolved instances.
*
* @return void
*/
public function flush()
{
$this->aliases = [];
$this->resolved = [];
$this->bindings = [];
$this->instances = [];
}
If I comment out
$this->instances = [];
Everything works perfectly and there are no more error messages.
Does that help anyone explain these? I'm out of my depth.
Thanks.
@kfirba Just a ping to see if you ever managed to sort this properly?
I had this problem, apparently I had a @dataProvider I was using and I named the function "setup" - because this was the function in the class I was testing.
Renaming this works now
@jonnywilliamson Not really. It was a long time ago. If I remember correctly, I just created a new Laravel project and it just worked there..
In case this helps someone coming across this issue, I had this in a Lumen application today. After some investigation and playing around, I found that it was because in PHPStorm it was adding the --no-configuration
option onto the phpunit
command because I hadn't configured my PHPUnit setup for the project in the IDE.
I corrected that by clicking 'Run > Edit Configurations' and then under 'Defaults > PHPUnit' click the little button to the far right of the 'Use alternative configuration file:' option and set the 'Default configuration file:' to the full path to your project's phpunit.xml
.
Hope this helps!
@simonhamp You have saved me a big headache - thank you :).
@rattfieldnz cool! Glad I shared that :)
Had same problem and i've deleted bootstrap/cache/config.php and ran php artisan clear-compiled , now it's working fine
Extending on Edit 3 this how I fixed this issue
// tests/TestCase.php
protected function tearDown()
{
$config = app('config');
parent::tearDown();
app()->instance('config', $config);
}
@oniryx Thank you you saved my day!!
One little thing: For me it threw an error on the tearDown method unless I used the : void
protected function tearDown() : void
Nearly 2 years later, I come across this exact same issue again. First search on Google was this closed issue, and then seen I've been here before. So i can confirm @simonhamp 's response (https://github.com/laravel/framework/issues/9733#issuecomment-339732813) still works :).
Just want to note that in Laravel 5.8.35 with PHPUnit 7.5.17 this works...
protected function setUp(): void
{
parent::setUp();
// Do something
}
protected function tearDown(): void
{
// Do something
parent::tearDown();
}
Note: it's critical to to run you code _after_ parent::setUp(); and _before_ parent::tearDown(); this was the only way I could get it to work.
I'm getting this error as well by testing out middleware that uses a config value. I've tried @oniryx 's fix but it didn't work out for me. I'm using Laravel 6.7.0 and php 7.4.0
EDIT Make sure you're extending Laravel's testcase instead of PHPUnit's. That was my mistake and the whole reason my test wasn't working.
For anyone stumbling upon this ticket, but none of the suggestions out there helped: There seems to be an issue with ignition where it seems to have a problem with dump
statements in unit tests:
There is an open ticket over at the ignition
repo, where you might want to leave a thumbs up 👍
"Target class [config] does not exist" exception when using dump()
I was trying to clean up the database in a tearDownAfterClass
and could only do it once I avoided calling the default tearDown
.
protected function tearDown(): void {}
public static function tearDownAfterClass(): void {
self::cleanUp();
}
Are you using DatabaseTransactions or DatabaseMigrations?
On Tue, Jan 21, 2020 at 1:45 PM Gavin Palmer notifications@github.com
wrote:
I was trying to clean up the database in a tearDownAfterClass and could
only do it once I avoided calling the default tearDown.protected function tearDown(): void {} public static function tearDownAfterClass(): void { self::cleanUp(); }
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/laravel/framework/issues/9733?email_source=notifications&email_token=AAGGACHLVUHOJXIT4VL2FZDQ647GBA5CNFSM4BL34L3KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJQZ6GI#issuecomment-576823065,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAGGACEGJUGC246JQCNXXHTQ647GBANCNFSM4BL34L3A
.
--
Jarret Minkler
Guys just add use Tests\TestCase instead of default LaravelTestCase , it's helps for my Laravel 6 :)
@jonnywilliamson and @oniryx's solution did the job, but I've chosen the latter, after fixing the issue bellow
Fatal error: Declaration of Tests\TestCase::tearDown() must be compatible with
Illuminate\Foundation\Testing\TestCase::tearDown(): void
in /app/tests/TestCase.php on line 11
// tests/TestCase.php
protected function tearDown():void
{
$config = app('config');
parent::tearDown();
app()->instance('config', $config);
}
I have learned that for every new Target class [<SOMETHING>] does not exist
I can expand this solution to cover the new SOMETHING error
Currently I have
// tests/TestCase.php
protected function tearDown():void {
$instances_names = ['config', 'url', 'request', 'html', 'Illuminate\Contracts\Http\Kernel'];
$instances = [];
foreach($instances_names as $instance_name):
$instances[$instance_name] = app($instance_name);
endforeach;
parent::tearDown();
foreach($instances as $instance_name => $instance):
app()->instance($instance_name, $instance);
endforeach;
}
It even works for Target [Illuminate\Contracts\Http\Kernel] is not instantiable.
!
In my case the problem was following line in .env file:
APP_BASE_PATH=/app
Because on Docker and on the server the real base path was var/www
. So, in the boostrap/app.php
the following line sets the applications base path:
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
Because of it, the first run of the test goes OK, and the 2nd test crashes with following error:
Symfony\Component\Finder\Exception\DirectoryNotFoundException: The "" directory does not exist.
In my case everything was fine and all tests were running smoothly. Until I introduced a second database connection configuration which I needed.
Now I faced the same weird "Class config does not exist" error in subsequent test using the second database configuration. Everything else is still running, but not the tests for the second config.
Anyway... this solution worked for me as well. Thx a lot!
Extending on Edit 3 this how I fixed this issue
// tests/TestCase.php protected function tearDown() { $config = app('config'); parent::tearDown(); app()->instance('config', $config); }
Most helpful comment
Extending on Edit 3 this how I fixed this issue