Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Ookma-Kyi's avatar

Test failing with Session is missing expected key [errors]

I have this test that checks that the active field is either 0 or 1:

it('requires valid data', function ($active) {
    // Arrange
    Belt::factory()->create([
        'min_xp' => 0,
    ]);

    $character = Character::factory()->create();

    $this->actingAs($character->user)
        ->put(route('characters.update', ['character' => $character]), ['active' => $active])
        ->assertInvalid('active');
})->with([
    [['active' => null], 'active'],
    [['active' => true], 'active'],
    [['active' => 1.5], 'active'],
    [['active' => '!!!!'], 'active'],
    [['active' => '    '], 'active'],
    [['active' => str_repeat('a', 16)], 'active'],
    [['active' => str_repeat('a', 2)], 'active'],
]);

However it is failing with Session is missing expected key [errors].. Here is my Vue:

<template>
    <AppLayout title="My Characters">
            <div class="grid row mt-5">
                <div class="col-span-6 m-auto">
                    <div class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
                        <h1 class="text-center mb-3">
                            <i aria-hidden="true"></i>  Characters
                        </h1>
                        <div v-if="characters.length === 0">
                            <p>You have no characters, why not create a new character?</p>
                            <Link :href="route('characters.create')" class="inline-block border border-blue-500 rounded py-1 px-3 bg-blue-500 text-white" role="link" title="Create a new character">Create a new character</Link>
                        </div>
                        <table v-else class="table-auto border-spacing-2 text-center">
                            <thead>
                            <tr>
                                <th scope="col">Name</th>
                                <th scope="col">Active</th>
                                <th scope="col">Options</th>
                            </tr>
                            </thead>
                            <tbody>
                            <tr v-for="character in characters" :key="character.id">
                                <th scope="row">{{ character.name }}</th>
                                <td><span v-if="character.active">X</span></td>
                                <td>
                                    <Link :href="route('characters.show', {character: character})" class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Show</Link>&nbsp;
                                    <Link v-if="!character.active" @click="switchCharacter(character.id)" class="text-sm text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Make active</Link></td>
                            </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
    </AppLayout>
</template>
<script setup>
import AppLayout from "@/Layouts/AppLayout.vue";
import {Link} from "@inertiajs/vue3";

defineProps(['characters']);

const switchCharacter = (characterId) => {
    fetch(`/characters/${characterId}`, {
        method: 'PUT',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
        },
        body: JSON.stringify({})
    })
        .then(response => {
            console.log(response); // Log the response for debugging
            if (response.ok) {
                // Handle success, maybe update the UI
                alert('Character switched successfully!');
                window.location.reload(); // Reload the page to reflect changes
            } else {
                // Handle error
                alert('Failed to switch character.');
            }
        })
        .catch(error => {
            console.error('Error:', error);
            alert('Failed to switch character.');
        });
}
</script>

And here is my route action:

    public function update(Request $request, Character $character)
    {
        // if the character is already the active
        if ($character->active) {

            // then redirect to the characters index
            return redirect('characters');
        }

        // get the player's active character if any
        $activecharacter = Character::where('user_id', auth()->user()->id)
            ->where('active', 1)
            ->first(['id', 'active']);

        // if the play has an active character
        if ($activecharacter) {

            // mark the previous character as not active
            $activecharacter->update(['active' => 0]);
        }

        // set the selected character as the active one
        $character->update(['active' => 1]);

        // then redirect to the characters index
        return response(200);
    }

I am using this as my base. Any ideas?

0 likes
2 replies
LaryAI's avatar
Level 58

The issue you're encountering with the test failing due to the session missing the expected key [errors] is likely because the validation errors are not being properly triggered or handled in your controller. To ensure that validation errors are correctly returned, you need to validate the request data before proceeding with the update logic.

Here's a step-by-step solution to address the problem:

  1. Add Validation to the Controller: Ensure that the active field is validated before any other logic is executed in the update method of your controller.
use Illuminate\Validation\Rule;

public function update(Request $request, Character $character)
{
    // Validate the request data
    $request->validate([
        'active' => ['required', Rule::in([0, 1])],
    ]);

    // if the character is already the active
    if ($character->active) {
        // then redirect to the characters index
        return redirect('characters');
    }

    // get the player's active character if any
    $activecharacter = Character::where('user_id', auth()->user()->id)
        ->where('active', 1)
        ->first(['id', 'active']);

    // if the player has an active character
    if ($activecharacter) {
        // mark the previous character as not active
        $activecharacter->update(['active' => 0]);
    }

    // set the selected character as the active one
    $character->update(['active' => 1]);

    // then redirect to the characters index
    return response(200);
}
  1. Update the Test: Ensure that the test is correctly structured to handle the validation errors. The assertInvalid method should be used to check for validation errors.
it('requires valid data', function ($active) {
    // Arrange
    Belt::factory()->create([
        'min_xp' => 0,
    ]);

    $character = Character::factory()->create();

    $this->actingAs($character->user)
        ->put(route('characters.update', ['character' => $character]), ['active' => $active])
        ->assertSessionHasErrors('active');
})->with([
    [null],
    [true],
    [1.5],
    ['!!!!'],
    ['    '],
    [str_repeat('a', 16)],
    [str_repeat('a', 2)],
]);
  1. Ensure Proper Error Handling in the Frontend: Make sure that your frontend is prepared to handle validation errors if they occur. This might involve displaying error messages to the user.

By adding validation to your controller and ensuring your test is correctly checking for validation errors, you should be able to resolve the issue with the session missing the expected key [errors].

Ookma-Kyi's avatar

@LaryAI Unfortunately, the issue persists. If I add validation the route no longer works. Additionally, there is no actual input to validate because there is no actual input required by the user besides clicking on a link/button which already has the character id baked in.

Please or to participate in this conversation.