framework: "Class config does not exist" (or any other class) when called in test environment PHPUnit
TL;DR
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' => 'john@example.com',
'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' => 'john@example.com'
], $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…
Edit:
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();
}
}
Edit 2:
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' => 'john@example.com',
'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' => 'john@example.com',
'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.
Edit 3:
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.
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Reactions: 2
- Comments: 25 (5 by maintainers)
Please ask on the fourms.
Just want to note that in Laravel 5.8.35 with PHPUnit 7.5.17 this works…
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.
Extending on Edit 3 this how I fixed this issue
Had same problem and i’ve deleted bootstrap/cache/config.php and ran php artisan clear-compiled , now it’s working fine
Guys just add use Tests\TestCase instead of default LaravelTestCase , it’s helps for my Laravel 6 😃
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 👍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
[SOLUTION] You have to use this class
Tests\TestCase
and notPHPUnit\Framework\TestCase
so your class will look like this:The issue can be caused by two reasons:
you forget to include parent:setUp() into your child setUp function; Your setUp function should look like this: function setUp() { parent::setUp(); //some other actions… }
in your child test class in the function tearDown() you called parent::tearDown() before some other code: function tearDown() { parent::tearDown(); //some other actions… }
That is incorrect!!!
You have to do like this: function tearDown() { //some other actions… parent::tearDown(); }
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 thephpunit
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!
@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
When my tests run, even though they all pass, I’m left with this as an error message at the end:
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 parentparent::flush
which is inIlluminate\Container\Container.php
Which finally runs this code:
If I comment out
Everything works perfectly and there are no more error messages.
Does that help anyone explain these? I’m out of my depth.
Thanks.
In my case everything works fine if I run:
but if I run tests in parallel:
I get the error:
I tried everything noted above but it didn’t work. Any help highly appreciated.
Edit:
I found code that causes problem:
Illuminate\Container\Container::flush
Of course we cannot change source code of framework but we can override
flush
:app/Application.php
bootstrap/app.php
I don’t like this solution but now my tests work in parallel. Still, I would like to have better solution without hacking framework behavior.
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.
@jonnywilliamson and @oniryx’s solution did the job, but I’ve chosen the latter, after fixing the issue bellow
I have learned that for every new
Target class [<SOMETHING>] does not exist
I can expand this solution to cover the new SOMETHING errorCurrently I have
It even works for
Target [Illuminate\Contracts\Http\Kernel] is not instantiable.
!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 😃.
@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
@rattfieldnz cool! Glad I shared that 😃
@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…