Reputation: 6318
What is the best way to write unit test for code which gets current time? For example some object might be created only at business days, other objects take into account current time when checking permissions to execute some actions, etc.
I guess that I should mock up the Date.today and and Time.now. Is this right approach?
Update: Both solutions (a) Time.is and (b) Time.stubs(:now).returns(t) work. (a) is very nice approach but (b) solution will be more consistent with other test code.
At this question an author asks for a general solution. For Ruby, in my option two above solutions are simpler and therefore better than extracting code which gets current date / time.
BTW I recommend to use Chronic to get required time, e.g.
require 'Chronic'
mon = Chronic.parse("next week monday")
Time.stubs(:now).returns(mon)
Upvotes: 5
Views: 1895
Reputation: 4335
Mocking Time.now or Date.today seems straightforward enough, would look something like:
require 'rubygems'
require 'test/unit'
require 'mocha'
class MyClass
def foo
Time.now
end
end
class MyTest < Test::Unit::TestCase
def test_foo
assert true
t = Time.now
Time.expects(:now).returns(t)
assert_equal t, MyClass.new.foo
end
end
Upvotes: 3
Reputation: 14716
The following is from Jay Field's Thoughts. It allows you to redefine Time.now
for the duration of a block.
require 'time'
class Time
def self.metaclass
class << self; self; end
end
def self.is(point_in_time)
new_time = case point_in_time
when String then Time.parse(point_in_time)
when Time then point_in_time
else raise ArgumentError.new("argument should be a string or time instance")
end
class << self
alias old_now now
end
metaclass.class_eval do
define_method :now do
new_time
end
end
yield
class << self
alias now old_now
undef old_now
end
end
end
Time.is(Time.now) do
Time.now # => Tue Nov 13 19:31:46 -0500 2007
sleep 2
Time.now # => Tue Nov 13 19:31:46 -0500 2007
end
Time.is("10/05/2006") do
Time.now # => Thu Oct 05 00:00:00 -0400 2006
sleep 2
Time.now # => Thu Oct 05 00:00:00 -0400 2006
end
Upvotes: 6
Reputation: 1106
Creating an ITimeProvider object as a dependancy is better than passing the time in because it fits the Don't Repeat Yourself principle.
Somewhere in your production code, something has to get the current time. You could take the date generation code outside the boundaries of your test coverage or you can have a single easily testable object that can be everywhere else.
Upvotes: 0
Reputation: 5813
Right off the top of my head, I'd guess that the best approach to this would be to not let your object get the time itself. In other words, pass the date/time into whatever method is being called on the object that's using the built-in time constructs currently. This could, depending on your circumstances, be a much simpler solution than mocking up Date.today and Time.now, as you suggest.
Edit: I say this in sharp contrast to the suggestion of having an ITimeProvider interface that you pass in as a dependency...that's just overkill, in my opinion.
Upvotes: 3
Reputation: 518
Pass a ITimeProvider (for example) class to your routine to use to get the time, then you can mock it and use the mock object to always give you a consistent time for the routine to use.
Upvotes: 3