PHPUnit: Expected status code 200 but received 419 with Laravel
Sometimes in testing you will need to disable middlewares to proceed :
use Illuminate\Foundation\Testing\WithoutMiddleware;
class ClassTest extends TestCase
{
use WithoutMiddleware; // use this trait
//tests here
}
and if you want to disable them just for one specific test use :
$this->withoutMiddleware();
The message here is indeed related to the CSRF middleware. But there is a much better way of attacking this problem than disabling the middleware.
The middleware comes with code built-in that detects if it is being used in a test. This check looks for 2 things:
- Am I being ran via a command line
- Am I being ran in an environment type of
testing
Default, proper setup of the software correctly causes both flags to be true when running PHP unit. However, the most likely culprit is the value in your APP_ENV
. Common ways for this to to be incorrect include:
- Misconfigured
phpunit.xml
file. It should contain<server name="APP_ENV" value="testing" />
- A shell session that has an explicit value set in
APP_ENV
that overrides this value - A Docker/docker-compose/kubernetes session that has an explicit value set in
APP_ENV
. Seeing about getting this value set via the .env and/or phpunit.xml files is perhaps better if possible. Or ensuring the build/test process sets the value.
This one stumped me as well and I was not convinced that I would need the use of WithoutMiddleware
since I did not for a different project, but it turned out I had experimented with something on the command line and overrode APP_ENV
in bash.
This is the exact solution:
Laravel environment will set after bootstraping application, so you cant change it from appServiceProvider or another source. for fix this error you need to add this function to App\Http\Middleware\VerifyCsrfToken
public function handle($request, \Closure $next)
{
if(env('APP_ENV') !== 'testing')
{
return parent::handle($request, $next);
}
return $next($request);
}
you need to use env('APP_ENV') that is set in .env.testing file with
APP_ENV=testing
Solution: When you cached your configuration files you can resolve this issue by running php artisan config:clear
.
Explanation: The reason why this can resolve the issue is that PHPUnit will use the cached configuration values instead of the variables defined in your testing environment. As a result, the APP_ENV
is not set to testing, and the VerifyCsrfTokenMiddleware
will throw a TokenMismatchException
(Status code 419).
It won't throw this exception when the APP_ENV
is set to testing since the handle
method of VerifyCsrfTokenMiddleware
checks if you are running unit tests with $this->runningUnitTests()
.
It is recommended not to cache your configuration in your development environment. When you need to cache your configuration in the environment where you are also running unit tests you could clear your cache manually in your TestCase.php
:
use Illuminate\Support\Facades\Artisan;
public function createApplication()
{
....
Artisan::call('config:clear')
....
}
Example based on https://github.com/laravel/framework/issues/13374#issuecomment-239600163
Read more about configuration caching in this blog post or the Laravel documentation.