Synchro's avatar

Testing logins with CSRF

I have this test, which is one of the standard ones from Breeze:

    public function test_users_can_authenticate_using_the_login_screen()
    {
        $user = User::factory()->create();
        $response = $this->post('/login', [
            'email' => $user->email,
            'password' => 'password',
        ]);
        $this->assertAuthenticated();
        $response->assertRedirect(RouteServiceProvider::HOME);
    }

This fails because the attempt to log in responds with a 419 because there is no CSRF token provided with the request. I tried adding a $this->get('/login'); before the POST, but that didn't help. I don't see how this could ever work since it doesn't try to get hold of a CSRF token before the POST, so I don't understand why this test is written this way. FWIW the login blade template does have a @csrf directive in.

Is there some standard way to disable CSRF during tests, or some way I should get hold of the token before making the request?

0 likes
7 replies
martinbean's avatar

@synchro Laravel tests should automatically handle CSRF tokens. Otherwise everyone’s test suite would be broken and no one would be able to issue a POST request without first having to retrieve a valid CSRF token.

It sounds like you’ve maybe changed something or disabled something if Laravel is not automatically handling CSRF tokens for you in tests.

Synchro's avatar

To check whether it's my doing or not, I just set up a clean install of Laravel, installed breeze, configured it using an empty sqlite DB, and ran the default tests – it fails authentication with the same 419 error. What setting might affect whether the test env uses CSRF or not?

SilenceBringer's avatar

@synchro possible you changed environment in your phpunit.xml? It must be testing

<env name="APP_ENV" value="testing"/>
Synchro's avatar

I've not touched that file, and it is set to testing.

Synchro's avatar

Tests\TestCase, but that only contains:

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;
}
Synchro's avatar
Synchro
OP
Best Answer
Level 2

I finally found the problem. I have a .env.testing config file to use during tests, however, my phpunit.xml file overrides some of the settings in there, but in particular it overrides the database connection type, substituting sqlite for my configured mysql. It turns out that some of my migrations are incompatible with sqlite (drops multiple columns in one modification), but because PHPUnit aggressively suppresses error output, this underlying problem was hidden, and it took a lot of debugging to expose this error. What was especially annoying is that when you run an individual test within PHPStorm, it doesn't seem to use this db config override, so the individual test was passing, but the same test would fail when run via artisan test. I repeated a clean Laravel install with Jetstream, and the default test suite did pass, so I don't know what was wrong with my previous attempt to do that.

Please or to participate in this conversation.