charles-wc's avatar

csrf_token(): Return value must be of type string, null returned

I am writing a Pest test for a Volt component, and this is the error I'm receiving when trying to run the test.

Here is my test

it('prefills patient and default date/time on mount', function () {
    // Ensure the expected patient exists
    Patient::factory()->create(['id' => 12]);

    // Freeze time to ensure deterministic "nextWeekday at 8:00"
    Carbon::setTestNow(Carbon::parse('2025-01-01 12:00:00')); // Wednesday

    // Replace with your actual view name (e.g., 'appointments.create')
    $component = Volt::test('appointments.create');

    $expected = Carbon::today()->nextWeekday()->setHour(8)->setMinute(0);

    $component
        ->assertSet('form.date', $expected->format('Y-m-d'))
        ->assertSet('form.time', $expected->format('H:i'));

    // Assert patient bound in the form is the one with id 12
    $patient = $component->get('form.patient');
    expect($patient)->not->toBeNull()
        ->and($patient->id)->toBe(12);

    Carbon::setTestNow();

});

Where it's failing is here: $component = Volt::test('appointments.create');

And the error I'm getting looks like this:

Illuminate\View\ViewException: csrf_token(): Return value must be of type string, null returned

The @csrf directive is included in appointments.create, and also as a meta tag. I've tried with one or the other and both. Laravel's documentation claims "For convenience, the CSRF middleware is automatically disabled for all routes when running tests."

Thoughts?

1 like
6 replies
LaryAI's avatar
Level 58

The error:

csrf_token(): Return value must be of type string, null returned

means that when your Volt component (or Blade view) calls csrf_token(), it returns null instead of a string. This usually happens because the application key is not set, or the session is not properly initialized in the test environment.

Why does this happen in tests?

  • In a normal HTTP request, Laravel's middleware ensures the session is started and a CSRF token is generated.
  • In a component test (like Volt or Livewire), if the session is not started, csrf_token() returns null.

Solution

1. Ensure the session is started in your test

Add the withSession() method before rendering the component:

$component = Volt::test('appointments.create')->withSession([
    '_token' => csrf_token() ?: Str::random(40),
]);

Or, if you want to ensure a token is always present:

use Illuminate\Support\Str;

$component = Volt::test('appointments.create')->withSession([
    '_token' => Str::random(40),
]);

2. Make sure the APP_KEY is set in your .env.testing

If APP_KEY is missing, Laravel can't generate a CSRF token. Run:

php artisan key:generate --env=testing

Or manually set APP_KEY in your .env.testing file.


Example: Full Test

use Illuminate\Support\Str;

it('prefills patient and default date/time on mount', function () {
    Patient::factory()->create(['id' => 12]);
    Carbon::setTestNow(Carbon::parse('2025-01-01 12:00:00')); // Wednesday

    $component = Volt::test('appointments.create')->withSession([
        '_token' => Str::random(40),
    ]);

    $expected = Carbon::today()->nextWeekday()->setHour(8)->setMinute(0);

    $component
        ->assertSet('form.date', $expected->format('Y-m-d'))
        ->assertSet('form.time', $expected->format('H:i'));

    $patient = $component->get('form.patient');
    expect($patient)->not->toBeNull()
        ->and($patient->id)->toBe(12);

    Carbon::setTestNow();
});

Summary

  • Use withSession(['_token' => ...]) when testing components that use @csrf or csrf_token().
  • Ensure your APP_KEY is set in the testing environment.

This should resolve the csrf_token(): Return value must be of type string, null returned error in your Volt component test.

merdin's avatar

Hi @charles-wc ,

I try to test Livewire (using FilamentPHP) components and I'm getting the exact same error.

Code:

it('renders the total relations widget on dashboard', function () {
    $user = createUserWithPermissions([]);
    actingAs($user);

    livewire(Dashboard::class)
        ->assertStatus(200)
        ->assertSee('Total Relations');
});
merdin's avatar
merdin
Best Answer
Level 5

It works when I do this instead: actingAs($user)->withSession(['_token' => 'abc']);

I don't know if this is the correct solution for it.

1 like
merdin's avatar

Now I removed the ->withSession() part and it works normally.

I tried to do:

  • php artisan optimize:clear
  • php artisan key:generate
  • composer update

And after that it seems to work.

1 like
charles-wc's avatar

@merdin Thank you so much! I moved the withSession() part and it's working now.

You are a life saver!

1 like

Please or to participate in this conversation.