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

kmnurunnabi's avatar

assertSessionDoesntHaveErrors() is not working

I have that following test:

/** @test */
    public function user_can_login_with_valid_credentials()
    {
        User::factory()->create([
            'email' => '[email protected]',
            'password' => 'password',
        ]);

        $this->post('/login', [
            'email' => '[email protected]',
            'password' => 'password',
        ])
            ->assertStatus(302)
            ->assertSessionDoesntHaveErrors();
    }

But its throwing:


1) Tests\Feature\Auth\LoginTest::user_can_login_with_valid_credentials
Session has unexpected errors:

{
    "default": [
        "The provided credentials do not match our records."
    ]
}
Failed asserting that true is false.

while the credential was right but why assertSessionDoesntHaveErrors() is not working as expected?

0 likes
23 replies
gych's avatar

It doesn't work because the session has errors. Can you show the code of your user factory? This might be related to password hash.

kmnurunnabi's avatar

@gych See

public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'username' => fake()->firstName() . fake()->randomNumber(3),
            'email' => fake()->unique()->safeEmail(),
            'bio' => fake()->realText(25),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'remember_token' => Str::random(10),
        ];
    }

That is what in my UserFactory class.

gych's avatar

@kmnurunnabi Just use this for the password in your factory:

'password' => Hash::make('password'),

You can then update your test like this:

    public function user_can_login_with_valid_credentials()
    {
        User::factory()->create([
            'email' => '[email protected]',
        ]);

        $this->post('/login', [
            'email' => '[email protected]',
            'password' => 'password',
        ])
            ->assertStatus(302)
            ->assertSessionDoesntHaveErrors();
    }

And your factory like this:

public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'username' => fake()->firstName() . fake()->randomNumber(3),
            'email' => fake()->unique()->safeEmail(),
            'bio' => fake()->realText(25),
            'email_verified_at' => now(),
            'password' => Hash::make('password'),
            'remember_token' => Str::random(10),
        ];
    }

By doing this password will always be the default password for users created with the factory. No need to add the password while creating a user via factory in your tests.

kmnurunnabi's avatar

@gych Interesting. I did just what you've said. But still the error meet as same.

gych's avatar

@kmnurunnabi Ok this can be related to the cast in your user Model, can you show your model?

kmnurunnabi's avatar

@gych That is the $casts in User model:

protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
    ];
gych's avatar

@kmnurunnabi This looks good and your first code should have also worked with this cast.

Does login work when you try it manually ? The cause of this might be coming from somewhere else in your code.

kmnurunnabi's avatar

@gych Yes Login work as expected. assertRedirect() also working and it's redirecting me to home. But in testing only assertSessionDoesntHaveErrors() not working.

kmnurunnabi's avatar

@gych I have a test written

/** @test */
    public function user_can_login()
    {
        $this->post('/login', [
            'email' => '[email protected]',
            'password' => 'password',
        ])
            ->assertStatus(302)
            ->assertRedirectToRoute('home');

        $this->assertAuthenticated();
    }

is also working. Noted that [email protected] is set inside setUp method.

gych's avatar

I also use assertAuthenticated to check if login works because this will also return errors when validation failed.

When you want to check if a user can't login with a wrong password you can use this, it checks if the user is still guest and not authenticated after posting the login

$this->assertGuest();

But its strange that everything else works and you get errors from assertSessionDoesntHaveErrors() Because it should also work when the other cases are working.

When you add assertSessionDoesntHaveErrors to user_can_login test, does it then still work?

kmnurunnabi's avatar

@gych It's really weird assertAuthenticated working. That's mean I am authenticated then why assertSessionDoesntHaveErrors is failing?

When I use assertGuest that's also failed.

gych's avatar

@kmnurunnabi assertGuest will only fail when you use for example a wrong password because then you are not authenticated and still a guest

When you use the correct login details the user will be authenticated and assertGuest will fail because the user is not a guest anymore.

Its strange that assertSessionDoesntHaveErrors still throws errors when authentication seems to work fine. Do you use a testing db for tests and refresh the db for each test?

kmnurunnabi's avatar

@gych I also do so. Sqlite as the database and I use RefreshDatabase trait. My all other tests working as expected. But I had trouble only for this one.

gych's avatar

@kmnurunnabi Can you try and change your test code to this?

        $user = User::factory()->create([
            'password' => 'password',
        ]);

        $response = $this->post('/login', [
            'email' => $user->email,
            'password' => 'password',
        ]);

        $response->assertStatus(302);
        $response->assertSessionDoesntHaveErrors();
kmnurunnabi's avatar

@gych I have written another test:

/** @test */
    public function user_can_login_with_valid_credentials()
    {
        $this->post('/login', [
            'email' => '[email protected]',
            'password' => 'password',
        ])
            ->assertStatus(302)
            ->assertSessionMissing('email')
            ->assertSessionHasInput('email')
            ->assertSessionHasErrors('email');
        // ->assertSessionDoesntHaveErrors();
        $this->assertAuthenticated();
    }

that passing. Can you imagine why this test is passing?

gych's avatar

@kmnurunnabi Its passing because your session has an error for the email field. Its not really good to use ->assertSessionHasErrors('email'); in this test, because when login is successful it shouldn't have this error. Its better to figure out where this error stored in session is coming from.

Can you show the code of your login/auth controller store method?

kmnurunnabi's avatar

@gych Sure here is this:

public function store(LoginRequest $request): RedirectResponse
    {
        if (! $request->authenticate()) {
            return back()->withErrors([
                'email' => 'The provided credentials do not match our records.',
            ])->onlyInput('email');
        }

        $request->session()->regenerate();

        return redirect()->intended(RouteServiceProvider::HOME);
    }

This is a bit modification of laravel breeze.

gych's avatar

@kmnurunnabi

Your issue is caused because of this part, this doesn't catch the errors from authenticate()

        if (! $request->authenticate()) {
            return back()->withErrors([
                'email' => 'The provided credentials do not match our records.',
            ])->onlyInput('email');
        }

By default Breeze just uses this and it should also work to catch the wrong credentials

$request->authenticate()

Not sure why you need to return these errors by returning withErrors for a test? And to catch errors from authenticate() the code should be in a try and catch block

        try {
            $request->authenticate();
        } catch (\Exception $e) {
            return back()->withErrors([
                'email' => 'The provided test credentials do not match our records.',
            ])->onlyInput('email');
        }

Just think about if you really need this, because it seems unnecessary and not needed for testing.

It looks like you added this extra part of code specifically for your login test and this is bad practise. You should test the code like its used when a real user logs in to your platform

kmnurunnabi's avatar

@gych wait if you wanna see my authenticate logic:

public function authenticate()
    {
        $this->ensureIsNotRateLimited();

        if (! Auth::attempt($this->validated(), $this->boolean('remember'))) {
            RateLimiter::hit($this->throttleKey());

            return false;
        }

        RateLimiter::clear($this->throttleKey());
    }

I have customised a bit where I just return boolean.

gych's avatar
gych
Best Answer
Level 29

@kmnurunnabi Ok I see what you're trying to do now

You also have to return true in authenticate() when the Auth::attempt is successfull

    public function authenticate()
    {
        $this->ensureIsNotRateLimited();

        if (!Auth::attempt($this->validated(), $this->boolean('remember'))) {
            RateLimiter::hit($this->throttleKey());

            return false;
        }

        RateLimiter::clear($this->throttleKey());
        return true;
    }

May I ask for what reason you are changing these default methods?

kmnurunnabi's avatar

@gych Thanks man it works. 👍..... The reason for this modification is I had some requirements to change the default login and registration view and the way authenticate method were set, wasn't working as expected in web.

1 like

Please or to participate in this conversation.