密码验证在两个相反的场景中失败

密码验证在两个相反的场景中失败

问题描述:

我正在通过Michael Hartl的Ruby on Rails教程开展工作,并产生了一个有趣的困境。我会做出错误的事情,所以我需要你的帮助来找到问题。密码验证在两个相反的场景中失败

问题围绕User模型中的密码属性的验证。此属性的初始验证是:

validates :password, presence: true, 
         confirmation: true, 
         length: { minimum: 6 } 

这需要密码的最小长度,并且旨在满足新用户创建其实例的情况。

我已经创建了以下测试(我希望我已经使用了Rspec!)。这些测试检查验证工作:

test "password must not be blank or made up of spaces" do 
    @user.password = @user.password_confirmation = " " 
    assert_not @user.valid? 
end 

test "password must not be empty/nil" do 
    @user.password = @user.password_confirmation = "" 
    assert_not @user.valid? 
end 

因此,我们正在检查密码字段不能包含空格或零条目。通过当前的验证,这些测试通过。一切都很好。

我已经发展到允许用户编辑他们的个人资料。这使用户可以更改他们的名字,电子邮件地址和密码/确认,如果他们选择。为了让用户更改其密码,如果他们不想要,额外的验证被添加到模型中的密码属性,增加allow_blank: true如:

validates :password, presence: true, 
         confirmation: true, 
         length: { minimum: 6 }, 
         allow_blank: true # added this! 

因此,用户可以现在如果他们不想更改他们的个人资料,他们在编辑他们的个人资料时将两个密码字段留空。这满足了测试:

test "successful edit" do 
    log_in_as @user 
    get edit_user_path(@user) 
    assert_template 'users/edit' 
    name = "Foo Bar" 
    email = "[email protected]" 
    patch user_path(@user), params: { user: { name: name, 
              email: email, 
              password: "", 
              password_confirmation: "" } } 
    assert_not flash.empty? 
    assert_redirected_to @user 
    @user.reload 
    assert_equal @user.name, name 
    assert_equal @user.email, email 
end 

这使得用户编辑只是他们的名字&电子邮件,并通过他们留下两个密码字段为空,没有必要改变自己的password。如上所示,这会在长时间通过测试中引发FAIL,例如:

test "password must not be blank or made up of spaces" do 
    @user.password = @user.password_confirmation = " " 
    assert_not @user.valid? 
end 

测试失败,因为用户已验证。略有不同的测试,测试对于nil,不是空白,通过:

test "password must not be empty/nil" do 
    @user.password = @user.password_confirmation = "" 
    assert_not @user.valid? 
end 

所以“”密码被捕获但“ “密码工作正常创建新用户或编辑现有用户。

allow_blank: true添加到用户模型验证密码似乎已经造成了这种情况。所以,我被困在两次测试失败之间。如果我忽略allow_blank: true,测试失败(全测试上面粘贴):

test "successful edit" do 
. 
. 
    patch user_path(@user), params: { user: 
            { name: name, 
             email: email, 
             password: "", 
             password_confirmation: "" } } 
. 
    assert_equal @user.name, name 
    assert_equal @user.email, email 
end 

发送空白passwordpassword_confirmation测试失败,因为它不允许为空。

添加验证内allow_blank: true失败此测试:

test "password must not be blank or made up of spaces" do 
    @user.password = @user.password_confirmation = " " 
    assert_not @user.valid? 
end 

这失败允许用户用由空格的密码来创建。 A nil密码,即根本没有字符,是不允许的。该测试有效。

这使得我必须决定用户必须更改/重复他们的两个密码字段,如果他们编辑他们的个人资料,或者允许用户可以使用由一个空格组成的密码注册的场景或许多空间,如本次测试没有抛出:

test "password must not be blank or made up of spaces" do 
    @user.password = @user.password_confirmation = " " 
    assert_not @user.valid? 
end 

添加的allow_blank: true绕过这个测试或一般的验证。接受任何数量空间的password这是违反模型中的验证。这怎么可能?

任何想法如何更好地测试(除了使用Rspec!)。我向你提供更多的知识。

TIA。

[编辑]

的建议在下面的意见修改了我的测试套件绿色。这是由于套件不足。为了测试不成功的整合,建议代码测试一气呵成多种方案,如:

test "unsuccessful edit with multiple errors" do 
    log_in_as @user 
    get edit_user_path(@user) 
    assert_template 'users/edit' 
    patch user_path(@user), params: { user: 
            { name: "", 
             email: "[email protected]", 
             password: "foo", 
             password_confirmation: "bar" } } 
    assert_template 'users/edit' 
    assert_select 'div.alert', "The form contains 3 errors." 
end 

的关键部分在这里越来越期待错误的号码是否正确,这样assert_select给出正确的结果。我没有。错误应该是空白名称,无效的电子邮件格式,密码太短,pwd &确认不匹配。没有显示短密码的错误。

我决定抽出两个测试来证明验证密码长度和存在的失败。 allow_blank的要点是在编辑用户配置文件时允许密码&确认字段有没有,因此在每次编辑用户配置文件时都不一定要输入密码。这些测试包括:

test "unsuccessful edit with short password" do 
    log_in_as @user 
    get edit_user_path(@user) 
    assert_template 'users/edit' 
    patch user_path(@user), params: { user: 
            { name: @user.name, 
             email: "[email protected]", 
             password: "foo", 
             password_confirmation: "foo" } } 
    assert_select 'div.alert', "The form contains 1 error." 
end 

test "unsuccessful edit with blank (spaces) password" do 
    log_in_as @user 
    get edit_user_path(@user) 
    assert_template 'users/edit' 
    patch user_path(@user), params: { user: 
            { name: @user.name, 
             email: "[email protected]", 
             password: " ", 
             password_confirmation: " " } } 
    assert_select 'div.alert', "The form contains 1 error." 
end 

如果密码改变,那么验证规则应适用,即密码不应该是空白,必须有一个最小长度。这不是教程本书建议的代码或使用on: :createon: :edit的修改代码。

+1

https://*.com/questions/5123972/ruby-on-rails-password-validation考虑在这个问题上的答案。 – MisterCal

+0

在这个问题中有一些可选的实现@MisterCal。除了试图找到一个可行的解决方案,OP还提出了一个问题,即现有的验证最小长度和非空白度的方法是在'BCrypt'应该是代码中的一个点添加'allow_blank:true'来重写为新用户捕获非空白。事实上,'update'行动并没有绕过验证,因为新用户将(应该)已经遵守验证规则并且具有验证的密码。那么,这个添加如何允许创建一个与验证规则相反的新用户呢? – OnlySteveH

+2

您只能指定'on::create'语句。然后创建另一个验证,该验证在::edit'上没有'allow_blank'语句。 – MisterCal

我想到了这一点,所以我在这里张贴,以防其他人遇到类似的问题。

我修改了验证,在User上包含:update操作,而不仅仅是:edit。这涵盖了保存到数据库的操作,并捕获了短密码更新验证,但仍允许由空格组成的密码。

检查documentation的位向我展示了使用allow_blank: true允许nil和由空格的字符串。这里的场景想要一个nil密码是可以接受的,但不是空白的。 allow_nil: true的替代验证更适合此处的场景。

从上面更新的代码看起来像,在User.rb

validates :password, presence: true, 
         length: { minimum: 6 }, 
         allow_nil: true, 
         on: [:edit, :update] 

validates :password, presence: true, 
         confirmation: true, 
         length: { minimum: 6 }, 
         on: :create 

扩展的测试套件是现在所有的绿色。