首页 > 解决方案 > 测试 Laravel 密码重置

问题描述

我正在实现https://laravel.com/docs/8.x/passwords的 Laravel 文档中描述的密码重置功能。我重置密码的方法如下:

    public function doPasswordReset(Request $request)
    {
        $request->validate([
            'token' => 'required',
            'email' => 'required|email',
            'password' => 'required|min:8|confirmed',
        ]);
    
        $status = Password::reset(
            $request->only('email', 'password', 'password_confirmation', 'token'),
            function ($user, $password) {
                $user->forceFill([
                    'password' => Hash::make($password)
                ]);
                //remember token not needed
                //->setRememberToken(Str::random(60));
    
                $user->save();
    
                event(new PasswordReset($user));
            }
        );
    
        return $status === Password::PASSWORD_RESET
                    ? redirect()->route('login')->with('status', __($status))
                    : back()->withErrors(['email' => [__($status)]]);
    }

我对这种方法的测试是:

    /** @test */
    public function the_user_can_update_their_password()
    {

        ParentUser::factory()->create([
            'email' => 'user@domain.com',
            'password' => Hash::make('oldpassword')
        ]);

        $token = Password::createToken(ParentUser::first());

        Password::shouldReceive('reset')
            ->once()
            ->withSomeofArgs([
                'email' => 'user@domain.com',
                'password' => 'newpassword',
                'password_confirmation' => 'newpassword',
                'token' => $token
            ])
            ->andReturn(Password::PASSWORD_RESET);

        $response = $this->post(route('password.update'), [
            'email' => 'user@domain.com',
            'password' => 'newpassword',
            'password_confirmation' => 'newpassword',
            'token' => $token
        ]);

        $response->assertRedirect(route('login'));

        //failures from here
        $this->assertEquals(Hash::make('newpassword'), Hash::make(ParentUser::first()->password));
        $this->assertNotEquals(Hash::make('oldpassword'), Hash::make(ParentUser::first()->password));

        Event::fake();
        Event::assertDispatched(PasswordReset::class, ParentUser::first());
    }

我的问题是最后三个断言失败了。我意识到这种情况正在发生,因为我的模拟没有调用闭包来影响密码更改并引发事件。所以我的问题是是否可以模拟函数调用的一些参数。

我正在考虑的一种解决方案是将闭包分解为自己的方法并单独测试。在没有其他任何东西的情况下,我认为这可能是唯一的方法。

标签: phplaraveltestingmocking

解决方案


Hash::make()不会为任何给定值生成相同的哈希。看一看:

Hash::make('newpassword') == Hash::make('newpassword')
// false

由于这是false,我希望assetEquals()在这里失败,因为它们的值不相等。相反,看看Hash::check()方法:

Hash::check('newpassword', Hash::make('newpassword'))
// true

您可以assertTrue()改为使用它,例如:

$this->assertTrue(Hash::check('newpassword', ParentUser::first()->password));
$this->assertFalse(Hash::check('oldpassword', ParentUser::first()->password));

旁注,->password应该已经是 Hashed,所以没有必要将它包装在另一个中Hash::make(),因为你会有一个“Hash-of-a-hash”,这会使Hash::check().


推荐阅读