密码验证在两种相反的情况下失败

时间:2017-11-03 23:40:43

标签: ruby-on-rails integration-testing bcrypt railstutorial.org ruby-on-rails-5

我正在研究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 = "foo@valid.co.uk"
  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。如上所述,这会导致长时间通过测试失败,例如:

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

此失败允许使用由空格组成的密码创建用户。不允许使用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: "foo@invalid",
                                      password: "foo",
                                      password_confirmation: "bar" } }
  assert_template 'users/edit'
  assert_select 'div.alert', "The form contains 3 errors."
end

这里的关键部分是使预期错误的数量正确,以便assert_select给出正确的结果。我没有。错误应该是空白名称,电子邮件格式无效,密码太短,pwd&确认不符合。短密码错误未显示。

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

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: "foo@valid.com",
                                      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: "foo@valid.com",
                                      password: " ",
                                      password_confirmation: " " } }
  assert_select 'div.alert', "The form contains 1 error."
end

如果密码 已更改,则应应用验证规则,即密码不应为空白且必须具有最小长度。这不是在教程书建议的代码或使用on: :createon: :edit的修改代码中发生的事情。

1 个答案:

答案 0 :(得分:1)

我想出了这个,所以我发布在这里以防其他人遇到类似的问题。

我修改了验证,将:update操作包含在User上,而不仅仅是: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

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