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?
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)