Time Warp for Rails Testing
Update: This has been turned into a gem for plug-and-play reuse.
In my freelance time I find myself working with an internationalized site used in over 50 countries. Of course, users of the site wanted to deal with things in a localized time zone. Adding time zone awareness brings a special angle to testing and it saddled me with some errors I had not seen before.
To start, I made use of some excellent time warp code for the unit tests I had written up. It worked beautifully. That is, it worked beautifully until I ran
rake or, more specifically,
My functional tests were dying hard with these new, handy additions to
test_helper.rb. From the execution of the first test, I was seeing “stack trace too deep” errors.
./test/functional/../test_helper.rb:13:in `real_now': stack level too deep (SystemStackError) from ./test/functional/../test_helper.rb:13:in `real_now' from ./test/functional/cms/../../test_helper.rb:13:in `now' from /usr/local/lib/ruby/1.8/logger.rb:327:in `add' from /usr/local/lib/ruby/1.8/logger.rb:348:in `debug' from /Users/bjhess/Sites/iridesco/afs/trunk/config/../vendor/rails/actionpack/lib/action_controller/helpers.rb:114:in `default_helper_module!' from /Users/bjhess/Sites/iridesco/afs/trunk/config/../vendor/rails/actionpack/lib/action_controller/helpers.rb:124:in `send' from /Users/bjhess/Sites/iridesco/afs/trunk/config/../vendor/rails/actionpack/lib/action_controller/helpers.rb:124:in `inherited' from /Users/bjhess/Sites/iridesco/afs/trunk/config/../app/controllers/cms/email_controller.rb:1 ... 9 levels... from /Users/bjhess/Sites/iridesco/afs/trunk/config/../vendor/rails/activerecord/lib/../../activesupport/lib/active_support/dependencies.rb:489:in `load' from /usr/local/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake/rake_test_loader.rb:5 from /usr/local/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake/rake_test_loader.rb:5:in `each' from /usr/local/lib/ruby/gems/1.8/gems/rake-0.7.3/lib/rake/rake_test_loader.rb:5 rake aborted! Command failed with status (1): [/usr/local/bin/ruby -Ilib:test "/usr/local...]
The specific bit of code appeared to be the
real_now call on the line
real_now - testing_offset:
alias_method :real_now, :now def now real_now - testing_offset end alias_method :new, :now
My workaround was to move all of the time warp code into its own
ENV["RAILS_ENV"] = "test" require File.expand_path(File.dirname(__FILE__) + "/../config/environment") 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 class Test::Unit::TestCase def pretend_now_is(time) begin Time.testing_offset = Time.now - time yield ensure Time.testing_offset = 0 end end end
Then I simply included the
time_helper file along with
test_helper in any test that required time warping:
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../time_helper' class EventTest < Test::Unit::TestCase . . . end
This worked beautifully. Unfortunately I’m at a loss as to why the initial setup had problems and I really wish I did know why. I don’t like being in the dark on problems.