How to fake Time.now?

RubyUnit TestingTime

Ruby Problem Overview


What's the best way to set Time.now for the purpose of testing time-sensitive methods in a unit test?

Ruby Solutions


Solution 1 - Ruby

I really like the Timecop library. You can do time warps in block form (just like time-warp):

Timecop.travel(6.days.ago) do
  @model = TimeSensitiveMode.new
end
assert @model.times_up!

(Yes, you can nest block-form time travel.)

You can also do declarative time travel:

class MyTest < Test::Unit::TestCase
  def setup
    Timecop.travel(...)
  end
  def teardown
    Timecop.return
  end
end

I have some cucumber helpers for Timecop here. They let you do things like:

Given it is currently January 24, 2008
And I go to the new post page
And I fill in "title" with "An old post"
And I fill in "body" with "..."
And I press "Submit"
And we jump in our Delorean and return to the present
When I go to the home page
I should not see "An old post"

Solution 2 - Ruby

Personally I prefer to make the clock injectable, like so:

def hello(clock=Time)
  puts "the time is now: #{clock.now}"
end

Or:

class MyClass
  attr_writer :clock

  def initialize
    @clock = Time
  end

  def hello
    puts "the time is now: #{@clock.now}"
  end
end

However, many prefer to use a mocking/stubbing library. In RSpec/flexmock you can use:

Time.stub!(:now).and_return(Time.mktime(1970,1,1))

Or in Mocha:

Time.stubs(:now).returns(Time.mktime(1970,1,1))

Solution 3 - Ruby

I'm using RSpec and I did this: Time.stub!(:now).and_return(2.days.ago) before I call Time.now. In that way I'm able to control the time I used for that particular test case

Solution 4 - Ruby

Using Rspec 3.2, the only simple way I found to fake Time.now return value is :

now = Time.parse("1969-07-20 20:17:40")
allow(Time).to receive(:now) { now }

Now Time.now will always return the date of Apollo 11 landing on the moon.

Source: https://www.relishapp.com/rspec/rspec-mocks/docs

Solution 5 - Ruby

Do the time-warp

time-warp is a library that does what you want. It gives you a method that takes a time and a block and anything that happens in the block uses the faked time.

pretend_now_is(2000,"jan",1,0) do
  Time.now
end

Solution 6 - Ruby

Don't forget that Time is merely a constant that refers to a class object. If you're willing to cause a warning, you can always do

real_time_class = Time
Time = FakeTimeClass
# run test
Time = real_time_class

Solution 7 - Ruby

If you have ActiveSupport included, you could use:

travel_to Time.zone.parse('2010-07-05 08:00')

http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html

Solution 8 - Ruby

Also see this question where I put this comment as well.

Depending upon what you are comparing Time.now to, sometimes you can change your fixtures to accomplish the same goal or test the same feature. For example, I had a situation where I needed one thing to happen if some date was in the future and another to happen if it was in the past. What I was able to do was include in my fixtures some embedded ruby (erb):

future:
    comparing_date: <%= Time.now + 10.years %>
    ...

past:
    comparing_date: <%= Time.now - 10.years %>
    ...

Then in your tests then you choose which one to use to test the different features or actions based upon the time relative to Time.now.

Solution 9 - Ruby

Had the same issue, I had to fake time for a spec for a specific day and time just did that:

Time.stub!(:now).and_return(Time.mktime(2014,10,22,5,35,28))        

this will give you:

2014-10-22 05:35:28 -0700

Solution 10 - Ruby

This kind of works and allows for nesting:

class Time
  class << self
    attr_accessor :stack, :depth
  end

  def self.warp(time)
    
    Time.stack ||= [] 
    Time.depth ||= -1 
    Time.depth += 1
    Time.stack.push time

    if Time.depth == 0 
      class << self    
          alias_method :real_now, :now  
          alias_method :real_new, :new

          define_method :now do
            stack[depth] 
          end

          define_method :new do 
            now 
          end
      end 
    end 

    yield

    Time.depth -= 1
    Time.stack.pop 

    class << self 
      if Time.depth < 0 
        alias_method :new, :real_new
        alias_method :now, :real_now
        remove_method :real_new
        remove_method :real_now 
      end
    end

  end
end

It could be slightly improved by undefing the stack and depth accessors at the end

Usage:

time1 = 2.days.ago
time2 = 5.months.ago
Time.warp(time1) do 

  Time.real_now.should_not == Time.now

  Time.now.should == time1 
  Time.warp(time2) do 
    Time.now.should == time2
  end 
  Time.now.should == time1
end

Time.now.should_not == time1 
Time.now.should_not be_nil

Solution 11 - Ruby

Depending upon what you are comparing Time.now to, sometimes you can change your fixtures to accomplish the same goal or test the same feature. For example, I had a situation where I needed one thing to happen if some date was in the future and another to happen if it was in the past. What I was able to do was include in my fixtures some embedded ruby (erb):

future:
    comparing_date: <%= Time.now + 10.years %>
    ...

past:
    comparing_date: <%= Time.now - 10.years %>
    ...

Then in your tests then you choose which one to use to test the different features or actions based upon the time relative to Time.now.

Solution 12 - Ruby

i just have this in my test file:

   def time_right_now
      current_time = Time.parse("07/09/10 14:20")
      current_time = convert_time_to_utc(current_date)
      return current_time
    end

and in my Time_helper.rb file i have a

  def time_right_now
    current_time= Time.new
    return current_time
  end

so when testing the time_right_now is overwritten to use what ever time you want it to be.

Solution 13 - Ruby

I allways extract Time.now into a separate method that I turn into attr_accessor in the mock.

Solution 14 - Ruby

The recently-released Test::Redef makes this and other fakery easy, even without restructuring the code in a dependency-injection style (especially helpful if you're using other peoples' code.)

fake_time = Time.at(12345) # ~3:30pm UTC Jan 1 1970
Test::Redef.rd 'Time.now' => proc { fake_time } do
   assert_equal 12345, Time.now.to_i
end

However, be careful of other ways to obtain time that this will not fake out (Date.new, a compiled extension that makes its own system call, interfaces to things like external database servers which know current timestamps, etc.) It sounds like the Timecop library above might overcome these limitations.

Other great uses include testing things like "what happens when I'm trying to use this friendly http client but it decides to raise this an exception instead of returning me a string?" without actually setting up the network conditions which lead to that exception (which may be tricky). It also lets you check the arguments to redef'd functions.

Solution 15 - Ruby

My own solution https://github.com/igorkasyanchuk/rails_time_travel - a gem with UI, so you don't need to hardcode any datetime in the code. Just change it from the UI.

It might be also very useful for you QA's team, or testing app on the staging.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser147962View Question on Stackoverflow
Solution 1 - RubyJames A. RosenView Answer on Stackoverflow
Solution 2 - RubyAvdiView Answer on Stackoverflow
Solution 3 - RubyStaelenView Answer on Stackoverflow
Solution 4 - RubyIwazaruView Answer on Stackoverflow
Solution 5 - RubyBaroqueBobcatView Answer on Stackoverflow
Solution 6 - RubyAndrew GrimmView Answer on Stackoverflow
Solution 7 - RubyArturView Answer on Stackoverflow
Solution 8 - RubyBryan WardView Answer on Stackoverflow
Solution 9 - RubyNetta DView Answer on Stackoverflow
Solution 10 - RubySam SaffronView Answer on Stackoverflow
Solution 11 - RubyBryan WardView Answer on Stackoverflow
Solution 12 - RubyMo.View Answer on Stackoverflow
Solution 13 - RubyhakuninView Answer on Stackoverflow
Solution 14 - Rubyuser240438View Answer on Stackoverflow
Solution 15 - RubyIgor KasyanchukView Answer on Stackoverflow