ruby-on-railsrubytestingrspecactivemodel

How to prevent password from being updated if empty in Rails Model and test it with RSpec?


I use Bcrypt gem for password encryption.

User model:

class User < ApplicationRecord
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }, confirmation: true
  # ...
end

How to allow password to be blank when user is updated?:

expect(user.update(name: 'Joana', password: '')).to be(true)

But not update password?

expect { user.update(name: 'Joana', password: '') }
.not_to(change { user.reload.password_digest })

If you want to test it, mind to find user before updating it not to get false positives:

user = User.find(user.id)

Solution

  • class User < ApplicationRecord
      has_secure_password
      validates(
        :password, 
        allow_nil: { on: :update }, # add this
        length: { minimum: 6 }
      )
      # ...
    end
    

    Thus these expectations pass:

    user = User.find(user.id) # Refresh object as if you were in controller
    
    expect(user.update(name: 'Joana')).to be(true)
    expect(user.update(name: 'Joana', password: '')).to be(true)
    
    expect { user.update(name: 'Joana', password: '') }
    .not_to(change { user.reload.password_digest })
    

    However, mind it would not update with actual nil password:

    expect(user.update(name: 'Joana', password: nil)).to be(false)
    

    So, it is counter-intuitive but it works.

    Confirmation, and presence validations are not needed as they're provided by Rails.