Hello!
I have a unit test where I need to simulate a different system time.
Hence I need Time.now to return a different time than now, say, now + 1
month
How can I do this?
I tried aliasing Time now, but I don’t know how to alias class methods.
class Time
alias_method ‘self.original_now’, ‘self.now’
end
How can I alias this --or-- is there any better way to simulate a
different
time on calling Time.now?
Tips and ideas are greatly appreciated,
Rob
On 5/24/06, Robert MannI [email protected] wrote:
I tried aliasing Time now, but I don’t know how to alias class methods.
Tips and ideas are greatly appreciated,
Rob
Hi
class Time
class << self
alias_method :n, :now
end
end
hope that helps
Cheers
Robert
–
Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.
Thanks alot Robert, that’ll do it
Rob
One caution: I did what Robert suggested a while back. It works
perfectly, but does wreak havoc with Test::Unit and rake test times
reporting. Tests can appear to finish before they’ve started, etc. I
ended up creating a CurrentTime class:
class CurrentTime
@@now = nil
def now
@@now || Time.now
end
def now=(new_time)
@@now = new_time
end
end
I changed my code to use CurrentTime.now instead of Time.now.
Test::Unit, rake and friends still use Time.now, so they don’t get
messed up. In my unit test I mock out the time by calling
CurrentTime.now=. Crude but it works.
Steve
On 5/24/06, Molitor, Stephen L [email protected] wrote:
@@now || Time.now
works.
Yup you have to change the code to be tested, not the best idea.
I guess you can avoid this by redefining now only in the class to be
tested,
please note I did not hang anybody (by not redefining Time.now) just
gave
you the rope to hang yourself (by redefining Time.now).
Now (pun intended) I believe that you can redefine Time.now in the scope
where you need it without interfering with Test::Unit, well you are
still
administring the code you test somehow.
Neverheless that might be the cleanest approach, I would be glad to have
a
better one suggested though.
Cheers
Robert
Steve
Hence I need Time.now to return a different time than now, say, now
class << self
Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.
–
Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.
I believe that you can redefine Time.now in the scope where you need
it without interfering with Test::Unit
There’s only one instance of the Time class object, and any changes you
make to it are in effect ‘global’. At least in Ruby 1.8. I understand
there’s some talk of adding namespaces or something to be able to make
modifications and extensions to classes only apply in a certain scope in
Ruby 2.0.
Steve
Molitor, Stephen L wrote:
I believe that you can redefine Time.now in the scope where you need
it without interfering with Test::Unit
There’s only one instance of the Time class object, and any changes you
make to it are in effect ‘global’.
You can use a technique called “Constant Injection” where you
temporarily change the definition of a constant within the scope of a
single class.
See http://onestepback.org/articles/depinj/classesarejustobjects.html
for an example.
This allows you to say something like:
def test_my_class
MyClass.use_class(:Time, MockTime) do
mc = MyClass.new
# Any calls to Time.now within MyClass will be routed to
MockTime.now
end
# Calls to Time.now are now back to normal.
end
– Jim W.
On May 24, 2006, at 3:55 PM, Robert D. wrote:
Although I do not understand it (this is not an anomaly of the
universe
though), it does not look like it
please kindly look at this
You are declaring a new class named Time in your module. This may or
not work for the OP.
e.g.:
% cat overriding_time.rb
class Base
def self.something_invovling_time_now
Time.now
end
end
module Wrap
class Time
def self.now
“Whee”
end
end
end
class Child < Base
include Wrap
puts something_invovling_time_now
puts Time.now
end
% ruby overriding_time.rb
Wed May 24 16:42:11 EDT 2006
Whee
On 5/24/06, Jim W. [email protected] wrote:
Molitor, Stephen L wrote:
I believe that you can redefine Time.now in the scope where you need
it without interfering with Test::Unit
There’s only one instance of the Time class object, and any changes you
make to it are in effect ‘global’.
Although I do not understand it (this is not an anomaly of the universe
though), it does not look like it
please kindly look at this
------------------------------------ >8
robert@roma:~/ruby/tests$ cat test1.rb;./test1.rb
#!/usr/bin/env ruby
require ‘date’
module ToBTested
class Time
class << self
def now; “right now?”; end
end
end
puts "Inside module ToBTested: " << Time.now.to_s
end
puts "Outside module ToBTested: " << Time.now.to_s
Inside module ToBTested: right now?
Outside module ToBTested: Wed May 24 21:47:44 CEST 2006
--------------------------- 8<
*** HOWEVER ***
this theoretical possibility will probably not help the OP because
Test::Unit might not have access to the unaltered version of Time
(please
note, this is ruby-talk not physics-talk) when executing in Testcase
context.
I had no time (again) going through this during working hours but if
someone
would care to explain the exact location where the inconsistency occurs
withing Test::Unit I would gladly have a look.
Cheers
Robert
Very nice! If I understand correctly, I could do something like this:
class Module
def inject_constant(constant, value)
old_value = const_get(constant)
const_set(constant, value)
if block_given?
yield
const_set(constant, old_value)
end
end
end
Then in a test, I could do this:
def test_my_app
MyApp::inject_constant(:Time, MockTime)
end
If all of my code is in the module MyApp, then all of my code uses
MockTime whenever it refers to Time. Forever in this case, which might
be fine in a test. Or I could pass a block to limit the duration. Or
use setup / teardown. If I want to be more fine grained I can just
inject into one class.
In any case, I can mock out the current time without messing up external
code, and without changing my original code. All without a fancy DI or
AOP framework. Wow!
Is this the cleanest implementation of this technique?
I went through all your DI presentation slides. Looks like it was a
fantastic presentation.
Steve
On 5/24/06, Logan C. [email protected] wrote:
On May 24, 2006, at 3:55 PM, Robert D. wrote:
[snip]
You are declaring a new class named Time in your module.
Of course, I completely screwed up! Sorry for the noise.
This may or
puts something_invovling_time_now
puts Time.now
end
% ruby overriding_time.rb
Wed May 24 16:42:11 EDT 2006
Whee
–
Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.
Correction, I should probably wrap the user code in an ensure block to
ensure that the constant gets reset:
class Module
def inject_constant(constant, value)
old_value = const_get(constant)
const_set(constant, value)
if block_given?
begin
yield
ensure
const_set(constant, old_value)
end
end
end
end
Steve
Following up a slightly old thread here, but I’ve taken a hybrid
approach to a couple already suggested and it seems to work well:
class Time
@@now = nil
def self.now=(time)
@@now = time
end
def self.forced_now
@@now || unforced_now
end
class << self
alias_method :unforced_now, :now
alias_method :now, :forced_now
end
end
Then, in my test I can simply do Time.now = whatever. As long as I
put a Time.now = nil in my teardown, it doesn’t seem to mess up the
test timings. (Note that I’m doing this in Rails, so your mileage may
vary.) It’s important that the Time.now = nil goes in the teardown,
rather than in the test itself, to ensure that it runs even if a test
fails.
Cheers,
Pete Y.
http://9cays.com/