Program Club

Time.now를 가짜로 만드는 방법?

proclub 2020. 10. 31. 10:17
반응형

Time.now를 가짜로 만드는 방법?


Time.now단위 테스트에서 시간에 민감한 방법을 테스트 할 목적 으로 설정하는 가장 좋은 방법은 무엇입니까 ?


저는 Timecop 라이브러리를 정말 좋아합니다 . 시간 왜곡과 마찬가지로 블록 형식으로 시간 왜곡을 수행 할 수 있습니다.

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

(예, 블록 형태의 시간 여행을 중첩 할 수 있습니다.)

선언적 시간 여행을 할 수도 있습니다.

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

좀이 오이 타임 캅에 대한 도우미 여기를 . 다음과 같은 작업을 수행 할 수 있습니다.

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"

개인적으로 나는 시계를 다음과 같이 주입 가능하게 만드는 것을 선호합니다.

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

또는:

class MyClass
  attr_writer :clock

  def initialize
    @clock = Time
  end

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

그러나 많은 사람들이 조롱 / 스터 빙 라이브러리를 선호합니다. RSpec / flexmock에서는 다음을 사용할 수 있습니다.

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

또는 Mocha에서 :

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

저는 RSpec을 사용하고 있는데 Time.now를 호출하기 전에 Time.stub! (: now) .and_return (2.days.ago) 을했습니다. 그런 식으로 특정 테스트 케이스에 사용한 시간을 제어 할 수 있습니다.


Rspec 3.2를 사용하여 Time.now 반환 값을 가짜로 찾은 유일한 방법은 다음과 같습니다.

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

이제 Time.now는 항상 Apollo 11이 달에 착륙 한 날짜를 반환합니다.

출처 : https://www.relishapp.com/rspec/rspec-mocks/docs


할 일 타임 워프

시간 왜곡은 원하는 것을 수행하는 라이브러리입니다. 그것은 당신에게 시간과 블록이 걸리는 방법을 제공하며 블록에서 일어나는 모든 일은 가짜 시간을 사용합니다.

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

Time클래스 객체를 참조하는 상수 일뿐 임을 잊지 마십시오 . 기꺼이 경고를하고 싶다면 언제든지 할 수 있습니다.

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

또한 이 의견을 넣은 이 질문을 참조하십시오 .

당신이 무엇을 비교하는지에 따라 Time.now, 때로는 동일한 목표를 달성하거나 동일한 기능을 테스트하기 위해 조명기를 변경할 수 있습니다. 예를 들어, 어떤 날짜가 미래에있을 경우 한 가지 일이 발생하고 과거 일 경우 다른 일이 발생해야하는 상황이있었습니다. 내가 할 수 있었던 것은 내 비품에 루비 (erb)를 포함하는 것입니다.

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

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

그런 다음 테스트에서와 관련된 시간을 기준으로 다양한 기능 또는 작업을 테스트하는 데 사용할 항목을 선택합니다 Time.now.


같은 문제가 발생하면 특정 날짜와 시간에 대한 사양에 대한 시간을 가짜로 만들어야했습니다.

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

이것은 당신에게 줄 것입니다 :

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

ActiveSupport가 포함 된 경우 다음을 사용할 수 있습니다.

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

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


이런 종류의 작동하고 중첩을 허용합니다.

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

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.


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.


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


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.

참고URL : https://stackoverflow.com/questions/1215245/how-to-fake-time-now

반응형