Back

TestCase tests unable to make multiple call()


johnlamont posted 4 years ago

I have a class that extends TestCase. I had a setUp() that was doing a $this->call(...) and then in the test itself I was also doing a $this->call(...). This resulted in a Symfony\Component\HttpKernel\Exception\NotFoundHttpException exception. After much adjusting I isolated it to the following:

This works:

public function testFooBar(){
    $this->call('GET','dummy/route');
}

While this does not:

public function testFooBar(){
    $this->call('GET','dummy/route');
    $this->call('GET','dummy/route');
}

Can anyone tell me why, and is there any work around?

Thanks in advance.

petercoles replied 4 years ago

I suspect that there's else going in here. Although I'm not sure what the sue case is for repeated use of the call() method, I don't seem to have any problems making more than one.

The only way I could trigger the error you're seeing was if I tried to access a route that genuinely doesn't exist, rather than repeating one that does.

maknz replied 4 years ago

I'm also seeing this behaviour. Use case is needing to obtain a key first by doing one call, then doing the next call with that key has a header for an integration test. Commenting out the first call() makes the second call work, but fail with the unexpected status code. Leaving the first call() in causes the not found exception.

Edit: Doing a $this->refreshApplication() fixes it, just ensure that you set up the test again, e.g. call Route::enableFilters() again after the refresh if you had filters enabled originally. Obviously that's a hack though, I would guess some sort of state is being set with the first $this->call() which is messing with the second.

Here is the code requiring the hack. A call is made to api/v1/auth to create an API key to be used for requests, the key is then tested by calling an authenticated route api/v1/me.

public function testBasicAuthIntegration() {
    Route::enableFilters();

    $login = [
      'email' => '[email protected]',
      'password' => 'testing'
    ];

    $response = $this->call('POST', 'api/v1/auth', $login);
    $data = json_decode($response->getContent());

    $this->assertResponseStatus(201);

    $this->refreshApplication();
    Route::enableFilters();

    $hash = $data->hash;
    $server = [
      'PHP_AUTH_USER' => $hash
    ];

    $response = $this->call('GET', 'api/v1/me', [], [], $server);

    $data = json_decode($response->getContent(), true);

    $this->assertResponseOk();

    $this->assertArrayHasKey('email', $data);
    $this->assertSame($data['email'], $login['email']);

}
hervejodoin replied 4 years ago

I just had a similar issue. Struggled for hours to figure it out, the error messages were not super helpful.

It turns out the problem was that I was adding my view composers through require_once. It seems require_once does not play well with some PHPUnit tests.

Hope this can help someone someday...

Thatdoorsajar replied 3 years ago

hervejodoin said:

I just had a similar issue. Struggled for hours to figure it out, the error messages were not super helpful.

It turns out the problem was that I was adding my view composers through require_once. It seems require_once does not play well with some PHPUnit tests.

Hope this can help someone someday...

This helped me massively as I had separated my route file for a large project and was using include_once to pull them into route.php - Thanks hervejodoin

lamoni replied 3 years ago

Just confirming that changing require_once to require in my routes.php (which I was using to include separate route files) fixed this issue. Thanks!

sameerpanjwani replied 3 years ago

Same issue here, changed "include_once" to require in the routes.php file and the errors disappeared from phpunit

llaski replied 2 years ago

Same issue and fix for me - changing require_once to require in my routes.php solved it. My guess would be the global app instance is getting refreshed or reloaded for each test in some way but its running phpunit is one entire php process & require_once obv will only run once so after the first time the global app instance is losing all of the routes. Educated guess, have not spent what would prob be hours confirming that since I'm spending that time writing tests instead :)

jake-harris replied 2 years ago

Holy cow thank you guys. I have wasted days and days of my life debugging this issue. Changing require to require once in my multi-route-file inclusion method did the trick.

budhajeewa replied 2 years ago

Just piling up; same issue - same fix.

I think it's hight time we had a "proper" way of using multiple route files.

Murwa replied 1 year ago

hervejodoin said:

I just had a similar issue. Struggled for hours to figure it out, the error messages were not super helpful.

It turns out the problem was that I was adding my view composers through require_once. It seems require_once does not play well with some PHPUnit tests.

Hope this can help someone someday...

Saved me big time...was on my 4th hour, digging! Thanks man

simonhamp replied 6 months ago

Thanks @hervejodoin! Your answer helped me find the solution to my own problem too.

It's really important to remember that, although each method in a Laravel test class reboots the entire application (in tearDown and setUp), it's only mimicking separate requests, it's not actually individual requests through the web server.

This is why using require_once exhibits "weird" behaviour in certain situations. When your app is requested via the web server, it would typically initialise a separate PHP process for each request, but in PHPUnit all tests in a single test class are run in a single process (by default) and using require_once or include_once will mean those things won't get loaded multiple times.

That's because, when you use require_once/include_once, PHP keeps a track of which files were loaded and doesn't allow them to be loaded again.

In my case, I was loading a routes file with require_once, so when the application was torn down and set up, everything but my routes from that file were being rebuilt, causing a lot of frustration in my tests.


Sign in to participate in this thread!



We'd like to thank these amazing companies for supporting us