问题
I'm running a Rails 4 app with rspec-rails 3.0.0 and shoulda-matchers 2.5.0, and I've got an Event model that has some conditional validations like so:
class Event < ActiveRecord::Base
validates :name, :location, :time, :city, :zipcode, :user, :state, presence: true
validates :post_text, presence: true, if: :happened?
validates :pre_text, presence: true, unless: :happened?
belongs_to :community
belongs_to :user
belongs_to :state
def happened?
(self.time < Time.now) ? true : false
end
end
My event_spec.rb looks like:
require 'spec_helper'
describe Event do
before { @event = FactoryGirl.build(:future_event) }
subject { @event }
it { should validate_presence_of(:time) }
it { should validate_presence_of(:name) }
it { should validate_presence_of(:location) }
it { should validate_presence_of(:user) }
it { should validate_presence_of(:city) }
it { should validate_presence_of(:state) }
it { should validate_presence_of(:zipcode) }
it { should belong_to(:state) }
it { should belong_to(:user) }
it 'has pre_text if future event' do
expect(FactoryGirl.create(:future_event)).to be_valid
end
it 'has post_text if past event' do
expect(FactoryGirl.create(:past_event)).to be_valid
end
end
But when I run the tests, my it { should validate_presence_of(:time) }
block fails because it continues to run the remaining validations and throws an exception on the conditional validations because self.time is nil. I have implemented a hacky fix for this by checking time.present? in the happened? method. But can anyone help me understand what the best method for testing conditional validations that require the presence of another field would be?
回答1:
Yeah, unfortunately there isn't another way. The fact is that time
is technically allowed to be nil before you save your Event record, so if you happen to call #happened? before time
gets set, it will raise an exception. So if I wrote this code I would personally be okay with placing a check in #happened?, because it's a case you would need to handle anyway (regardless of the chance this will actually happen).
回答2:
This is definitely coming late, but for others searching for a solution. I found an effective way of doing this:
context "if happened" do
before { allow(subject).to receive(:happened?).and_return(true) }
it { is_expected.to validate_presence_of(:time) }
end
I hope this helps
回答3:
Just a small tip:
def happened?
(self.time < Time.now) ? true : false
end
is the same as
def happened?
self.time < Time.now
end
来源:https://stackoverflow.com/questions/22156073/how-to-test-conditional-validation-with-rspec-rails-3-0-0-and-shoulda-matchers-2