DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world
Time Warp For Functional And Unit Testing
Add this at the top of test/test_helper.rb (or elsewhere if not using rails):
# Extend the Time class so that we can offset the time that 'now'
# returns. This should allow us to effectively time warp for functional
# tests that require limits per hour, what not.
class Time #:nodoc:
class <<self
attr_accessor :testing_offset
alias_method :real_now, :now
def now
real_now - testing_offset
end
alias_method :new, :now
end
end
Time.testing_offset = 0
Add this method to Test::Unit::TestCase (in the class definition in test_helper.rb):
# Time warp to the specified time for the duration of the passed block
def pretend_now_is(time)
begin
Time.testing_offset = Time.now - time
yield
ensure
Time.testing_offset = 0
end
end
And now you can write time-based tests. For example:
def test_should_not_allow_more_than_3_requests_in_last_hour_from_same_ip
(1..3).each { |n| successful_request }
start_count = WorkOrderRequest.count
post :new, :work_order_request => REQUEST_TEMPLATE
assert_response :redirect
assert_redirected_to :controller => 'work_order_request',
:action => 'limit_exceeded'
assert_equal start_count, WorkOrderRequest.count
end
def test_should_not_allow_more_than_10_requests_in_last_24_hours_from_same_ip
10.downto(1) do |n|
pretend_now_is(n.hours.ago) do
successful_request
end
end
start_count = WorkOrderRequest.count
post :new, :work_order_request => REQUEST_TEMPLATE
assert_response :redirect
assert_redirected_to :controller => 'work_order_request',
:action => 'limit_exceeded'
assert_equal start_count, WorkOrderRequest.count
end






Comments
Snippets Manager replied on Wed, 2009/09/09 - 12:47pm
Snippets Manager replied on Fri, 2007/08/17 - 8:08am
Snippets Manager replied on Fri, 2007/08/17 - 8:08am
Snippets Manager replied on Thu, 2007/08/23 - 11:00pm
unless Time.respond_to? :real_now # prevent the error: stack level too deep (SystemStackError) class Time #:nodoc: class <Snippets Manager replied on Fri, 2007/08/17 - 8:08am
Snippets Manager replied on Fri, 2007/08/17 - 8:08am
./test/functional/../test_helper.rb:13:in `real_now': stack level too deep (SystemStackError)I moved all time specific code to a new time_helper.rb in /test:ENV["RAILS_ENV"] = "test" require File.expand_path(File.dirname(__FILE__) + "/../config/environment") # Extend the Time class so that we can offset the time that 'now' # returns. This should allow us to effectively time warp any # tests that require limits per hour, specific time borders, etc. class Time #:nodoc: class < Then, for any tests that need to simulate time offsets, I require the time_helper:require File.dirname(__FILE__) + '/../../time_helper'Snippets Manager replied on Thu, 2007/08/23 - 11:00pm
Time.testing_offset = 0The full pretend_now_is(time) becomes:# Time warp to the specified time for the duration of the passed block def pretend_now_is(time) begin Time.testing_offset = 0 Time.testing_offset = Time.now - time yield ensure Time.testing_offset = 0 end endA test case showing what I mean is this:require File.dirname(__FILE__) + '/../../test_helper' # To avoid the error: stack level too deep (SystemStackError) # => http://bjhess.com/bjhessblog/2007/08/12/time-warp-for-rails-testing/ require File.dirname(__FILE__) + '/../../time_helper' class WarpTest < Test::Unit::TestCase def setup Time.testing_offset = Time.now - Time.local(2007, 8, 15) # position all test back in time! end # ... many, many test that do NOT USE the pretend_now_is() # but one particular test needs to make some time jumping of its own def test_time_travel_machine pretend_now_is(Time.local(2007, 8, 7)) do puts Time.now # => 2007-08-16 ... NOT WHAT WE'D EXPECT!!! end end endSnippets Manager replied on Thu, 2007/10/04 - 11:26pm
Snippets Manager replied on Mon, 2006/08/21 - 6:57pm
class Time #:nodoc: class <Snippets Manager replied on Thu, 2007/08/23 - 11:00pm
def setup Time.testing_offset = Time.now - Time.local(2007, 8, 15) # position all test back in time! endso i've changed the original code a little (maybe a little too much :D) in order to: * stop exposing the "offset" accessor, create higher-level methods to manipulate it: Time#set(); Time#reset() * make sure all time jumps are absolute and not affected by any previous time warps * allow pretend_now_is() to be optionally called without a block, to change the time globally (in fact, the same as calling Time#set() directly) So here's the fully changed "test/time_helper.rb" (who knows, some might find it useful! :) There's also some test cases further down...ENV["RAILS_ENV"] = "test" require File.expand_path(File.dirname(__FILE__) + "/../config/environment") unless Time.respond_to? :real_now # prevent the error: stack level too deep (SystemStackError) # Test Helper: used only in testing! # # Extend the +Time+ class so that we can offset the time that +now+ # returns. This should allow us to effectively time warp for functional # tests that require limits per hour, what not. # # Example usage: # class WarpTest < Test::Unit::TestCase # # def setup # pretend_now_is(Time.local(2007, 8, 1)) # position *all* tests back in time! # end # # def teardown # Time.reset # jump back to the present # end # # # If one particular test needs some time jumping of its own... # def test_decades # pretend_now_is(Time.local(1960)) do # assert_equal(1960, Time.now.year) # end # pretend_now_is(Time.local(1970)) do # assert_equal(1970, Time.now.year) # end # # ... # end # end # # (see reference http://snippets.dzone.com/posts/show/1738) class Time class < And the tests "test/units/time_helper_test.rb":require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../time_helper' class TimeHelperTest < Test::Unit::TestCase def teardown Time.reset end def test_time_defaults_to_the_present Time.now assert_equal(0, Time.offset) end def test_pretend_with_a_block_applies_only_to_the_block current_year = Time.now.year pretend_now_is(Time.local(current_year + 10)) do assert_equal(current_year + 10, Time.now.year) end assert_equal(current_year, Time.now.year) end def test_pretend_without_a_block_changes_time_globally current_year = Time.now.year assert_equal(current_year, Time.now.year) pretend_now_is(Time.local(current_year - 10)) assert_equal(current_year - 10, Time.now.year) end def test_all_time_jumps_are_absolute_and_not_affected_by_any_previous_time_warps current_year = Time.now.year assert_equal(current_year, Time.now.year) pretend_now_is(Time.local(current_year - 5)) assert_equal(current_year - 5, Time.now.year) pretend_now_is(Time.local(current_year + 5)) assert_equal(current_year + 5, Time.now.year) end def test_reset_brings_us_back_to_the_preset current_year = Time.now.year pretend_now_is(Time.local(current_year + 10)) Time.reset assert_equal(current_year, Time.now.year) end end