【问题标题】:Testing Laravel Password Reset测试 Laravel 密码重置
【发布时间】:2021-10-22 03:20:53
【问题描述】:

我正在实现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());
    }

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

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

【问题讨论】:

  • Password::shouldReceive('reset') 这会模拟对reset 的调用,实际上不会更改密码。如果您将其注释掉,那么它可能会起作用。注意:由于您使用的是框架代码,我认为您不应该尝试验证密码是否已更改。只有 Password::reset 会被正确的参数调用。您可以假设 Laravel 框架测试已经测试过它可以按预期工作

标签: php laravel testing mocking


【解决方案1】:

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 应该已经被散列了,所以没有必要将它包装在另一个 Hash::make() 中,因为你会有一个“散列哈希”,这会使Hash::check() 失败.

【讨论】:

  • 没问题! Hash::check() 是一件很容易忘记的事情,但你能想象如果 Hash::make(...) == Hash::make(...)true ? 会更糟吗?很高兴你让它工作了,干杯!
【解决方案2】:

向@tim-lewis 和@apokryfos 提供帮助,他们都提供了帮助答案。以下是修改后的测试,现已通过。

@apokryfos 表示不应发生模拟调用,因为我们需要实际更改密码。

@tim-lewis 在他的回答中表明需要使用Hash::check() 方法检查哈希

    /** @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());

        Event::fake();

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

        dump(ParentUser::first()->password);

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

        $this->assertTrue(Hash::check('newpassword', ParentUser::first()->password));
        
        Event::assertDispatched(PasswordReset::class);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    • 2017-08-04
    • 2020-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多