Rspec 3 - 如何在模型测试中处理before_validation

时间:2014-09-22 08:47:18

标签: ruby-on-rails-4 factory-bot rspec-rails

在我的模型中,我有一个before_validation方法:

class Post < ActiveRecord::Base
 before_validation :generate_url
 ...

 private

def generate_url
   return unless self.url.blank?
   year = self.published_at.class == ActiveSupport::TimeWithZone ? self.published_at.year : DateTime.now.year
   self.url = "#{year}/#{self.title.parameterize}"
end

rspec的/模型/ post.spec.rb

测试w rspec-rails(3.1.0)和factory_girl_rails(4.4.1),我得到了一个spec / models / post_spec.rb来验证Post模型和spec / factories / post.rb

由于before_validation

验证测试失败

 Post should require title to be set
 Failure/Error: it { is_expected.to validate_presence_of(:title) }
 NoMethodError:
   undefined method `parameterize' for nil:NilClass
 # ./app/models/post.rb:69:in `generate_url'
 # ./spec/models/post_spec.rb:20:in `block (2 levels) in <top (required)>'

在generate_url方法中使用调试语句,我得到自己为nil ...所以看起来工厂帖子没有考虑在内

规格/模型/ post_spec.rb

require 'rails_helper'

RSpec.describe Post, :type => :model do
  it { is_expected.to validate_presence_of(:title) }
  it { is_expected.to ensure_length_of(:title).is_at_least(5) }
  it { is_expected.to validate_presence_of(:content) }
  it { is_expected.to ensure_length_of(:content).is_at_least(10) }
  it { is_expected.to validate_presence_of(:url) }
  it { is_expected.to validate_uniqueness_of(:url) }
  it { is_expected.to validate_presence_of(:published_at) }
end

规格/工厂/ post.rb

FactoryGirl.define do
  factory :post do
    user_id 1
    title "myTitle"
    content "myContent."
    published false
    published_at "2014-09-16 08:46:38"
  end
end
# note : attribute url is generated with before_validation

2 个答案:

答案 0 :(得分:5)

让它跳过before_validation

RSpec.describe Post, :type => :model do

  before{ Post.skip_callback(:validation, :before, :generate_url) }

  it { is_expected.to validate_presence_of(:user_id) }
  it { is_expected.to validate_presence_of(:title) }
  it { is_expected.to ensure_length_of(:title).is_at_least(5) }
  it { is_expected.to validate_presence_of(:content) }
  it { is_expected.to ensure_length_of(:content).is_at_least(10) }
  it { is_expected.to validate_presence_of(:url) }
  it { is_expected.to validate_uniqueness_of(:url) }
  it { is_expected.to validate_presence_of(:published_at) }
end

您还可以提供一个块,以便仅跳过块的验证,如下所示:

RSpec.describe Post, :type => :model do

  Post.skip_callback(:validation, :before, :generate_url) do 
    it { is_expected.to validate_presence_of(:user_id) }
    it { is_expected.to validate_presence_of(:title) }
    it { is_expected.to ensure_length_of(:title).is_at_least(5) }
    it { is_expected.to validate_presence_of(:content) }
    it { is_expected.to ensure_length_of(:content).is_at_least(10) }
    it { is_expected.to validate_presence_of(:url) }
    it { is_expected.to validate_uniqueness_of(:url) }
    it { is_expected.to validate_presence_of(:published_at) }
  end
end

答案 1 :(得分:0)

注意:自我不是零。 self.title是零

根据shoulda doc,可以通过设置这样的适当主题来修复规范

RSpec.describe Post, :type => :model do
  subject { FactoryGirl.create :post }
  it { is_expected.to validate_presence_of(:title) }
end

但是我尝试了这个,但错误仍然存​​在。 validate_presence_of只是忽略主题并将其重置为Post.new。它总是忽略了预期的事情。我会说这是一个错误的bug。 Wiil稍后检查他们的代码或至少提交问题。

与此同时,只要没有学校的方式就可以做到这一点

RSpec.describe Post, :type => :model do
    describe #valiadations do
      let!(:post) {Post.new.tap{|p|p.valid?} }
      it "validated title" do
        expect(post.errors_on[:title.size]).to eq(1) 
      end 
    end
 end