How to fake Time.now?

2019-01-10 12:04发布

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

14条回答
\"骚年 ilove
2楼-- · 2019-01-10 12:32

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"
查看更多
一纸荒年 Trace。
3楼-- · 2019-01-10 12:33

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.

查看更多
Ridiculous、
4楼-- · 2019-01-10 12:37

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

查看更多
唯我独甜
5楼-- · 2019-01-10 12:38

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
查看更多
老娘就宠你
6楼-- · 2019-01-10 12:38

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
查看更多
Bombasti
7楼-- · 2019-01-10 12:39

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.

查看更多
登录 后发表回答