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:
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.
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__) + ‘/../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.
Related posts:







Comments (10)
Congrats for the wonderful code! This time-based testing thing u wrote is great!
Unfortunately, I too was at a loss with the stack-thingy, and at ~3am the brain isn’t properly now… u can’t imagine the joy of getting the answer straight from on the one-and-only hit google gave me!
Love it when things work!!! :)
(… now, back to work!)
Thanks,
I must admit, though, that I didn’t originate the code. I found the base code here at snippets.dzone.com. (That’s an excellent site, by the way.)
My post was mainly to describe an error in using the code and my fix. I’m happy that helped you!
Yes, it was the snipped that I originally found, but thought you were the creator.
Nevertheless, your post was gold! ;)
I’ve also found minor glitch on the original cod, which I posted back to the snippet page. You might find it useful to take a look.
(ed: I fixed the link.)
Thanks. And thanks for sharing your other glitch. Although I wonder if it would be more appropriate to set the Time offset to 0 at the beginning of that test?
well, at least for me, “pretendnowis()” reads as something like absolute positioning the current time and not relative to the latest timeshift eventually made before…
.. but anyways, there’s always more than one way of reading :)
Mate, i think i might have found the “reason” for our +SystemStackError+ error. It reappeared using the `time_helper.rb` once I included it in _one-too-many_ test file.
It has something to do with getting into a recursive declaration look, when the Time class is redefined over and over again.
By making sure it only extends the Time class once, time error went away.
See the “unless” clause wrapping the Time definition in the “time_helper.rb”
(ed: fixed some markup)
(geee… :) I really can’t get your comments markup right!)
That worked beautifully. Thanks, hugocf! Make sure to correct things out at dzone.
As for formatting, I have Markdown running somewhere in here. I think that’s what got ya!
It sounds like you are really just trying to “stub” Time.now. If I were you I would use mocha (if you are still using Test::Unit) or use the fully integrated BDD framework, rSpec.
Both mocha and rspec are open source projects hosted on Rubyforge.
To “stub” the now class method on Time in rSpec you would do:
Time.stub!(:now).and_return(stored_time)
In mocha (though it has been a while since I used it so not sure of exact API) you could do something like:
Time.expects(:now).returns(stored_time)
HTH
Thanks for the advice!