dharga
dharga

Reputation: 2217

FactoryGirl Rspec creating invalid instances

So I have an application that revolves around events on a calendar. Events have a starts_at and ends_at. A new requirement has been added to restrict the creation of new events to only be in the future, more specifically >= midnight today. So naturally, I modified my validate method to check for this at instantiation. This however creates a problem when testing with RSpec using FactoryGirl.

A lot of tests require checking certain requirements with events in the past. Problem is, I can no longer create those elements in the past since it fails my validation. So I've found a few ways to get around this with

allow(instance)to receive(:starts_at).and_return(some_future_time)

or

expect_any_instance_of(Event).to receive(:check_valid_times).and_return(true)

Here's some simple code from the app

#Model
class Event < ActiveRecord::Base
  validate :check_for_valid_times

  private:
    def check_for_valid_times
      if starts_at.present? && (starts_at < Time.now.at_beginning_of_day)
        errors.add(:base, I18n.t('event.start_time_before_now'))
      end
      unless starts_at.present? && (starts_at < ends_at)
        errors.add(:base, I18n.t('event.end_time_before_start'))'
      end
    end
end

My factory

#factory
require 'factory_girl'

FactoryGirl.define do
  factory :event do
    starts_at { 2.hour.from_now.to_s }
  end
end

The question: Is there a nice clean way to do this instead of having to go handle each creation of an event and dealing with them specifically in all my tests? Currently I've having to go deal with quite a few of them in varying ways.

Let me know if there are other parts of the code I should be posting up.

As always, Thanks in advance for your help!

Upvotes: 1

Views: 214

Answers (1)

Daniel Evans
Daniel Evans

Reputation: 6808

The way I generally solve problems like this is the timecop gem.

For instance:

Timecop.freeze 1.hour.ago do
  create(:event)
end

You can then place these in let blocks, or in a helper in your spec/support/events.rb to keep it DRY and maintainable.

Using timecop, you can actually simulate a test completely. So create the event correctly at the time in the past that you care about, then return time to normal and see that your tests show that the event is in the past and cant be altered, and so on. It allows complete, accurate and understandable time-sensitive tests. RSpec helpers, shared contexts and before/let blocks can handle keeping the tests clean.

Upvotes: 2

Related Questions