Reputation: 79390
I want to create test data for an application, and there are a lot of time_at
attributes being tracked, too many to override in a maintainable way. What I'm thinking is, can I just change the base reference time variable in Ruby?
This would make it so created_at
, updated_at
, last_login_at
, etc., could be set to an artificial time, so I could do this in tests:
Date.today #=> Thu, 30 Dec 2010
Time.system_time_offset = 1.week.ago # made up
Date.today #=> Thu, 23 Dec 2010
Time.now #=> Thu Dec 23 14:08:38 -0600 2010
user_1 = User.create!
user_1.created_at #=> Thu Dec 23 14:08:38 -0600 2010
Time.reset_system_time # made up
user_2 = User.create!
user_1.created_at #=> Thu Dec 30 14:08:38 -0600 2010
Is there a way to do this?
Upvotes: 4
Views: 4105
Reputation: 192
A good gem for this is Timecop: https://github.com/travisjeffery/timecop.
You can freeze time or change the time (while it continues to progress) very easily.
Ex.
Time.now
# => 2014-03-14 13:17:02 -0400
Timecop.travel 2.hours.ago
Time.now
# => 2014-03-14 11:17:04 -0400
Its nicer than the mocha solution since all time functions will be affected equally, so you won't have a test where Time.now is returning something different then DateTime.now
Its also more up-to-date than the time-warp gem suggested in another answer.
Upvotes: 5
Reputation: 120
I use the timewarp gem for this sort of thing. You just put your code in a pretend_now_is(time) block and the code inside will be executed as if that was the actual time.
http://github.com/harvesthq/time-warp
Here's an example
def test_should_find_company_needing_reminded_today
pretend_now_is(Time.utc(2008,"jul",24,20)) do #=> Thu Jul 24 20:00:00 UTC 2008
@company.reminder_day = 'Thursday'
@company.save
companies = Company.find_companies_needing_reminded_today
assert_equal true, companies.include?(@company)
end
end
Upvotes: 3
Reputation: 26220
It's also possible to (yuck) monkeypatch Time:
$start = Time.now - 86400 # this time yesterday
class Time
class << Time
def new
$start
end
def now
Time.new
end
end
end
puts(Time.now)
puts($start)
Upvotes: 1
Reputation: 33239
Honestly, I usually write tests for current time to check if the timestamp is within a reasonable range. i.e., check if the timestamp is greater than 1.minute.ago
. Changing the system clock is likely to have all kinds of unpredictable side-effects, so you don't want to do that. You might be able to track down all the places in Ruby where the current time is accessed (though I think most methods just use Time.now
) and monkey-patch them for the tests, but I'd probably still prefer just checking the timestamp is within a sane range.
Upvotes: 1
Reputation: 34350
You could use Mocha to change the return value of Time.now during a test:
Time.stubs(:now).returns(Time.now - 1.day)
Upvotes: 7