user147962
user147962

Reputation: 721

How to fake Time.now?

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

Upvotes: 72

Views: 29976

Answers (15)

Igor Kasyanchuk
Igor Kasyanchuk

Reputation: 774

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.

Upvotes: 0

Artur INTECH
Artur INTECH

Reputation: 7366

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

Upvotes: 3

clemlatz
clemlatz

Reputation: 8063

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

Upvotes: 12

Netta D
Netta D

Reputation: 29

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

Upvotes: 1

BaroqueBobcat
BaroqueBobcat

Reputation: 10150

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

Upvotes: 10

user240438
user240438

Reputation:

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.

Upvotes: 0

James A. Rosen
James A. Rosen

Reputation: 65252

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"

Upvotes: 76

Andrew Grimm
Andrew Grimm

Reputation: 81548

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

Upvotes: 7

hakunin
hakunin

Reputation: 4231

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

Upvotes: 0

Mo.
Mo.

Reputation: 42563

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.

Upvotes: 0

Staelen
Staelen

Reputation: 7841

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

Upvotes: 14

Bryan Ward
Bryan Ward

Reputation: 6703

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.

Upvotes: 1

Bryan Ward
Bryan Ward

Reputation: 6703

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.

Upvotes: 0

Sam Saffron
Sam Saffron

Reputation: 131122

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

Upvotes: 0

Avdi
Avdi

Reputation: 18418

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

Upvotes: 45

Related Questions