Rspec be_valid在有效测试中失败

时间:2014-06-21 02:52:38

标签: ruby-on-rails rspec

我用Rails 4和rspec学习TDD。我为我的用户模型制作了一些测试用例来检查密码长度。到目前为止,我有两个测试,检查用户输入的密码是否太短,密码是否在6到10个字符之间。

到目前为止,密码太短了#34;测试通过:

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-z\d\-.]+\.[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 && 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

我在创建测试对象时对密码为零的原因感到困惑。

谢谢!

2 个答案:

答案 0 :(得分: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个字符时,您实际上并未测试密码被拒绝。

有关详细信息,请参阅this article on mass assignment

编辑:Leo Correa的评论可能暗示这可能不是你的情况。发布您的模型代码会有所帮助......

答案 1 :(得分: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 matchers

it { should_not allow_value('12345').for(:password) }
it { should allow_value('12345blah').for(:password) }