Question

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?

Was it helpful?

Solution 3

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

OTHER TIPS

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

Just a small tip:

def happened?
  (self.time < Time.now) ? true : false
end

is the same as

def happened?
  self.time < Time.now
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top