1

I have these questions while reading the Ruby On Rails Tutorial in here

The validation of User class is:

class User < ActiveRecord::Base
   before_save { self.email = email.downcase }
   validates :name, presence: true, length: { maximum: 50 }
   VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
   validates :email, presence: true, length: { maximum: 255 }
              format: { with: VALID_EMAIL_REGEX },
              uniqueness: { case_sensitive: false }
   has_secure_password
   validates :password, length: { minimum: 6 }, allow_blank: true
   .
   .
   .
end

In test,while patching a updated user information to the route of the user like this:

def setup
  @user = users(:michael)
end
.
.
.
test "successful edit" do
  get edit_user_path(@user)
  assert_template 'users/edit'
  name  = "Foo Bar"
  email = "foo@bar.com"
  patch user_path(@user), user: { name:  name,
                                email: email,
                                password:              "",
                                password_confirmation: "" }
  assert_not flash.empty?
  assert_redirected_to @user
  @user.reload
  assert_equal name,  @user.name
  email, @user.email
end

The test would pass and only the user's name and email would be updated and password won't change.

If the validation of the password doesn't include the "allow_blank:true",this test would fail.

So I don't understand that: When the test passed which means the password could be blank, why it wouldn't change the password to be blank? How could Rails know I just want to update some of the attributes?

Kent Lee
  • 107
  • 4
  • 13
  • watch out, `has_secure_password` already defines validations http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password – apneadiving May 22 '15 at 13:08
  • @apneadiving So, what's the use of allow_blank? – Kent Lee May 22 '15 at 13:28

1 Answers1

2

has_secure_password adds a password= setter method method to your model which discards empty? input when setting the password.

irb(main):012:0> "".empty?
=> true

This prevents users from choosing a blank password. If you dont want to take my word for it you can easily test this:

test "does not change password to empty string" do
  patch user_path(@user), user: { name:  name,
                                  email: email,
                                  password:              "",
                                  password_confirmation: "" }
  @user.reload
  assert_false @user.authenticate("")
end

However what your validation does do is that if the user sets a password it must be over 6 characters:

test "does not allow a password less than 6 characters" do
  patch user_path(@user), user: { name:  name,
                                  email: email,
                                  password:              "abc",
                                  password_confirmation: "abc" }
  assert assigns(:user).errors.key?(:password)
end

(PS. this is something that is better tested in a model test than a controller test)

max
  • 96,212
  • 14
  • 104
  • 165
  • I apologize if the tests are off, I almost exclusively use RSpec and typed them up out of memory. – max May 22 '15 at 13:39
  • Also as a side note you should really split your tests up. Best practice says one assertion per test, and testing several controller methods in the same example is a recipe for really bad tests. If you want to test the user path through the application use integration tests with something like Capybara. – max May 22 '15 at 13:45
  • 1
    Do you mean if I add the "allow_blank:true", the blank password would pass the test but won't be stored into database due to the has_secure_password ? – Kent Lee May 22 '15 at 13:56
  • 1
    Kind of, the `password=` setter makes it so that when you assign the params to the user the blank password is filtered out - so a blank password means that `password == nil`. , `allow_blank:true` tells rails to ignore the `length: { minimum: 6 }` validation if the password is blank or nil. – max May 22 '15 at 14:05
  • `has_active_password` only stores the `password_digest`. If the password is nil than `has_active_password` does not generate a new digest - so no new password is saved to the database. – max May 22 '15 at 14:08