获取当前时间的unit testing代码

为当前时间的代码编写unit testing的最佳方法是什么? 例如,某些对象可能仅在工作日创建,其他对象在检查执行某些操作的权限时会考虑当前时间等。

我想我应该嘲笑Date.today和Time.now。 这是正确的方法吗?

更新:两种解决方案(a)Time.is和(b)Time.stubs(:now).returns(t)工作。 (a)是非常好的方法,但(b)解决方案将与其他测试代码更加一致。

在这个问题上 ,作者要求提供一般解决方案。 对于Ruby,在我的选项中,上述两个解决方案更简单 ,因此比提取获取当前日期/时间的代码更好。

顺便说一下,我建议使用慢性来获得所需的时间,例如

require 'Chronic' mon = Chronic.parse("next week monday") Time.stubs(:now).returns(mon) 

模拟Time.now或Date.today似乎很简单,看起来像:

 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 

以下是Jay Field的思想 。 它允许您在块的持续时间内重新定义Time.now

 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 

将ITimeProvider(例如)类传递给您的例程以用于获取时间,然后您可以模拟它并使用模拟对象始终为您提供一致的时间来使用例程。

在我的脑海中,我猜想最好的方法就是不要让你的物体获得时间。 换句话说,将日期/时间传递给当前使用内置时间结构的对象上调用的任何方法。 根据您的具体情况,这可能是一个比模拟Date.today和Time.now更简单的解决方案,正如您所建议的那样。

编辑 :我说这与建议你将一个ITimeProvider接口作为依赖关系传递出来形成鲜明对比……在我看来,这只是矫枉过正。

创建一个ITimeProvider对象作为依赖性比通过时间更好,因为它符合“不重复自己”原则。

在生产代码中的某个地方,必须要获得当前时间。 您可以将日期生成代码置于测试覆盖范围之外,或者您可以拥有一个可以在其他任何地方使用的易于测试的对象。