#ruby-on-rails #rspec
#ruby-on-rails #rspec
Вопрос:
Я изучаю TDD с Rails 4 и rspec. Я сделал несколько тестовых примеров для своей пользовательской модели, чтобы проверить длину пароля. Пока у меня есть два теста, которые проверяют, вводил ли пользователь слишком короткий пароль, и тот, в котором пароль составляет от 6 до 10 символов.
Пока что тест «пароль слишком короткий» проходит:
it "validation says password too short if password is less than 6 characters" do
short_password = User.create(email: "tester@gmail.com", password: "12345")
expect(short_password).not_to be_valid
end
Однако в тесте, где у меня есть действительный пароль, он завершается с ошибкой:
it "validation allows passwords larger than 6 and less than 10" do
good_password = User.create(email: "tester2@gmail.com", password: "blahblah")
expect(good_password).to be_valid
end
И я получаю эту ошибку:
Failure/Error: expect(good_password).to be_valid
expected #<User id: 1, email: "tester2@gmail.com",
created_at: "2014-06-21 02:43:42", updated_at: "2014-06-21 02:43:42",
password_digest: nil, password: nil, password_hash: "$2a$10$7u0xdDEcc6KJcAi32LBW7uzV9n7xYbfOhZWdcOnU5Cdm...",
password_salt: "$2a$10$7u0xdDEcc6KJcAi32LBW7u"> to be valid,
but got errors: Password can't be blank, Password is too short (minimum is 6 characters)
# ./spec/models/user_spec.rb:12:in `block (3 levels) in <top (required)>'
Вот мой код модели:
class User < ActiveRecord::Base
has_many :pets, dependent: :destroy
accepts_nested_attributes_for :pets, :allow_destroy => true
VALID_EMAIL_REGEX = /A[w -.] @[a-zd-.] .[a-z] z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: true
validates :password, presence: true, :length => 6..10, :confirmation => true
#callbacks
before_save :encrypt_password
after_save :clear_password
#method to authenticate the user and password
def self.authenticate(email, password)
user = find_by_email(email)
if user amp;amp; user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
#method to encrypt password
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
#clears password
def clear_password
self.password = nil
end
end
Я не понимаю, почему пароль равен нулю при создании тестового объекта.
Комментарии:
1. В вашей модели должно быть несколько обратных вызовов, влияющих на способ вычисления пароля. Это видно из
password_hash
иpassword_salt
заполняется. Можете ли вы показать свой код модели?
Ответ №1:
У вас есть требование о наличии пароля в вашей модели, но затем у вас есть after_save
перехват, который аннулирует пароль и переводит запись в недопустимое состояние. Первый тест проходит, потому что ваши записи всегда переводятся в недопустимое состояние с помощью after_save
перехвата. Вам нужно переосмыслить, как вы обрабатываете хранилище паролей; как только вы решите эту проблему, вот несколько примеров кода, которые помогут вам несколько способов проверить это:
# Set up a :user factory in spec/factories.rb; it should look something like:
FactoryBot.define do
factory :user do
sequence(:email) { |n| "tester #{n}@gmail.com" }
password { SecureRandom.hex(6) }
end
end
# In your spec:
let(:user) { create :user, password: password }
context 'password' do
context 'length < 6' do
let(:password) { '12345' }
it { expect(user).not_to be_valid }
it { user.errors.message[:password]).to include('something') }
end
context 'length >= 6' do
context 'length < 10' do
let(:password) { 'blahblah' }
it { expect(user).to be_valid }
end
context 'length >= 10' do
let(:password) { 'blahblahblah' }
it { expect(user).not_to be_valid }
end
end
end
Вы также можете использовать средства сопоставления shoulda:
it { should_not allow_value('12345').for(:password) }
it { should allow_value('12345blah').for(:password) }
Ответ №2:
Наиболее вероятная проблема заключается password
в том, что поле не может быть массово назначено. Вот почему password
в выходном сообщении равно нулю.
Попробуйте это вместо:
it "validation allows passwords larger than 6 and less than 10" do
good_password = User.create(email: "tester2@gmail.com")
good_password.password = "blahblah"
expect(good_password).to be_valid
end
Обратите внимание, что ваш первый тест проходит случайно — у него та же проблема, что и у второго теста (пароль не присваивается). Это означает, что вы на самом деле не проверяете, что пароль отклоняется, когда в банкомате меньше 6 символов.
Более подробную информацию см. В этой статье о массовом назначении.
РЕДАКТИРОВАТЬ: комментарий Лео Корреа может предполагать, что это может быть не так для вас. Публикация вашего кода модели помогла бы…
Комментарии:
1. прошло 5 лет… Массовое присвоение было перенесено на уровень контроллера в Rails 4. Ничто (по умолчанию) не должно препятствовать присвоению любого поля в модели.