Rails 在用了 has_secure_password 后再给 password 添加了长度验证后出了 bug

varma · 2019年02月23日 · 最后由 varma 回复于 2019年02月24日 · 1167 次阅读

一开始我怀疑是其他地方出了问题,所以新建了一个项目,只写了 password 相关的测试一下,结果依然有问题 ruby 版本是 2.6.1 rails 版本是 5.2.2 模型是这样:

class User < ApplicationRecord
  has_secure_password

  validates :password, length: { in: 6..20 }
end

控制器是这样:

class UsersController < ApplicationController
  def show
  end

  def update
    @user = User.find(params[:id])
    if @user.update(user_params)
      redirect_to user_path(@user)
    end
  end

  private
    def user_params
        params.require(:user).permit(:name, :email, :password)
    end
end

模型测试:

class UserTest < ActiveSupport::TestCase
  setup do 
     @user = users(:one)
  end

  test 'should be true' do
    assert @user.valid?
  end

  test 'new user true?' do
    user = User.new(name: @user.name, email: @user.email, password: 'secret',
                           password_confirmation: 'secret')
    assert user.valid?
  end
end

控制器测试:

class UsersControllerTest < ActionDispatch::IntegrationTest
   setup do
     @user = users(:one)
   end

  test 'should show user' do
    get user_path(@user)
    assert_response :success
  end

  test "should update user" do
    patch user_path(@user), params: {user: {name: 'new_name'}}

    assert_equal 'new_name', @user.reload.name
  end
end

rails test 结果:

# Running:

..F

Failure:
UserTest#test_should_be_true [/mnt/c/code/test-password/test/models/user_test.rb:9]:
Expected false to be truthy.


bin/rails test test/models/user_test.rb:8

.F

Failure:
UsersControllerTest#test_should_update_user [/mnt/c/code/test-password/test/controllers/users_controller_test.rb:14]:
Expected: "new_name"
  Actual: "MyString"


bin/rails test test/controllers/users_controller_test.rb:11



Finished in 1.915078s, 2.6109 runs/s, 3.1330 assertions/s.
5 runs, 6 assertions, 2 failures, 0 errors, 0 skips

是版本的问题吗? 我在测试环境的控制台下试了下

2.6.1 :001 > u = User.first
  User Load (0.8ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
 => #<User id: 298486374, name: "MyString", email: "MyString", password_digest: "$2a$10$o8CKMpYB6KK3s1F9P9O2C.4SfXBrJ1i0Nbc3mQ5I/0h...", created_at: "2019-02-23 12:37:01", updated_at: "2019-02-23 12:37:01">
2.6.1 :002 > u.valid?
 => false
2.6.1 :003 > u.errors
 => #<ActiveModel::Errors:0x00007fffecf207d0 @base=#<User id: 298486374, name: "MyString", email: "MyString", password_digest: "$2a$10$o8CKMpYB6KK3s1F9P9O2C.4SfXBrJ1i0Nbc3mQ5I/0h...", created_at: "2019-02-23 12:37:01", updated_at: "2019-02-23 12:37:01">, @messages={:password=>["is too short (minimum is 6 characters)"]}, @details={:password=>[{:error=>:too_short, :count=>6}]}>
2.6.1 :004 > u.authenticate 'secret'
 => #<User id: 298486374, name: "MyString", email: "MyString", password_digest: "$2a$10$o8CKMpYB6KK3s1F9P9O2C.4SfXBrJ1i0Nbc3mQ5I/0h...", created_at: "2019-02-23 12:37:01", updated_at: "2019-02-23 12:37:01">

我把长度验证那行注释调后再进控制台执行 User.first.valid? 就返回 true 了。。。

password 字段,从数据库中查出来本身是空值;password_digest 这个值才不为空;所以 valid 会失败。

robertyu 回复

嗯,主要是 railstutorial 里面也是这么写的😂 ,我就依样画葫芦了。在 Stack Overflow 上找到了解决方法:在 password 的 validates 后面加上 allow_nil: true。加了之后不报错了,新加的长度验证的测试也能通过了。

varma 关闭了讨论。 04月12日 10:40
需要 登录 后方可回复, 如果你还没有账号请 注册新账号