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, rake test:functionals
.
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 time_helper.rb
file.
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.