Ruby on rails Rails时间与rspec不一致

Ruby on rails Rails时间与rspec不一致,ruby-on-rails,time,rspec,Ruby On Rails,Time,Rspec,我正在Rails中使用Time,并使用以下代码设置项目的开始日期和结束日期: start_date ||= Time.now end_date = start_date + goal_months.months 然后我克隆了对象,并编写rspec测试,以确认副本中的属性匹配。结束日期匹配: original[end_date]: 2011-08-24 18:24:53 UTC clone[end_date]: 2011-08-24 18:24:53 UTC 但是规范在开始日期上给

我正在Rails中使用Time,并使用以下代码设置项目的开始日期和结束日期:

start_date ||= Time.now
end_date = start_date + goal_months.months
然后我克隆了对象,并编写rspec测试,以确认副本中的属性匹配。结束日期匹配:

original[end_date]:  2011-08-24 18:24:53 UTC
clone[end_date]:     2011-08-24 18:24:53 UTC

但是规范在开始日期上给了我一个错误:

expected: Wed Aug 24 18:24:53 UTC 2011,
     got: Wed, 24 Aug 2011 18:24:53 UTC +00:00 (using ==)
很明显,日期是一样的,只是格式不同。它们最终如何以不同的方式存储在数据库中,我如何使它们匹配?我也用DateTime试过了,结果也一样

更正:结束日期也不匹配。它们打印出相同的结果,但rspec错误也会在上面显示出来。当我打印开始日期和结束日期时,值以不同的格式显示:

start date: 2010-08-24T19:00:24+00:00
end date: 2011-08-24 19:00:24 UTC

我最初的猜测是Time.now的格式与数据库值不同。

您应该模拟now时间方法,以确保它始终与规范中的日期匹配。您永远不知道延迟何时会导致规范失败,因为会有几毫秒。这种方法还将确保实际代码和规范上的时间是相同的

如果您使用的是默认的rspec mock lib,请尝试执行以下操作:

t = Time.parse("01/01/2010 10:00")
Time.should_receive(:now).and_return(t)

您确定使用的是
==
而不是
eql
be
?后两种方法使用对象标识,而不是比较值


从输出来看,预期值似乎是
时间
,而测试的值是
日期时间
。这也可能是一个问题,尽管考虑到Ruby的日期和时间库几乎是病态的,我不知道如何修复它……

这通常发生,因为rspec尝试匹配不同的对象:例如,时间和日期时间。同样,可比较的时间可能会有一些不同,比如几毫秒

在第二种情况下,正确的方法是使用stubing和mock。也看到

在第一种情况下,可能的解决方案是比较时间戳:

actual_time.to_i.should == expected_time.to_i
对于此类情况,我使用simple:

# ./spec/spec_helper.rb
#
# Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
#
#
# Usage:
#
# its(:updated_at) { should be_the_same_time_as updated_at }
#
#
# Will pass or fail with message like:
#
# Failure/Error: its(:updated_at) { should be_the_same_time_as 2.days.ago }
# expected Tue, 07 Jun 2011 16:14:09 +0300 to be the same time as Mon, 06 Jun 2011 13:14:09 UTC +00:00


RSpec::Matchers.define :be_the_same_time_as do |expected|
  match do |actual|
    expected.to_i == actual.to_i
  end
end

其中一个问题是Ruby Time对象的精度为纳秒,但大多数数据库的精度最高为微秒。解决此问题的最佳方法是将Time.now(或使用timecop)与一个整数存根。阅读我写的帖子:

我完全同意之前关于存根时间的回答。也就是说这里还有一件事。当您比较数据库中的日期时间时,您会损失一些ruby DateTime obj中的时间。在Rspec中以这种方式比较日期的最佳方法是:

database_start_date.should be_within(1).of(start_date)

我喜欢的一个解决方案是将以下内容添加到
spec\u helper

class Time
  def ==(time)
    self.to_i == time.to_i
  end
end

这样,即使在嵌套对象中,它也是完全透明的。

向这两个变量添加
。\u datetime
将强制日期时间值相等,并遵守时区和夏令时。对于仅日期比较,请使用
.to\u date

具有两个变量的规范示例:
actual_time.to_datetime.should==预期_time.to_datetime

更清晰的规范:
实际时间到日期时间应等于1个月从现在到日期时间

.to_i
在规范中对其含义产生歧义


+1用于在规范中使用TimeCop gem。如果您的应用程序受到DST的影响,请确保在您的规格中测试夏令时。

我们当前的解决方案是使用
冻结时间
方法来处理舍入:

def freeze_time(time = Time.zone.now)
  # round time to get rid of nanosecond discrepancies between ruby time and
  # postgres time
  time = time.round
  Timecop.freeze(time) { yield(time) }
end
然后你可以像这样使用它:

freeze_time do
  perform_work
  expect(message.reload.sent_date).to eq(Time.now)
end

根据您的规格,您可能可以使用Rails本机:

#在spec_helper.rb中
config.include ActiveSupport::Testing::TimeHelpers
开始日期| |=时间.当前.更改(usecs:0)
结束日期=开始日期+目标月。月
旅行开始日期做什么
#克隆在这里
结束
expect(克隆开始日期)。to eq(开始日期)

如果没有
Time.current.change(usecs:0)
它可能会抱怨时区之间的差异。或者在微秒之间,因为助手会在内部重置传递的值(Timecop也有类似的问题,所以也可以使用它重置
usecs

使用DateTime而不是Time时是否会得到相同的输出?即使使用
Timecop
,我也会遇到这个问题,这真的很奇怪。想法?结果证明这是我们持续集成环境中唯一的失败。似乎这是UTC与非UTC的问题,所以我只是将规范更改为
order.updated\u at.UTC.should==Time.now.UTC
,应该由它来解决。我的解决方案似乎不起作用。我们的CI环境仍然显示:Diff:2012-11-19 23:54:24 UTC.==(2012-11-19 23:54:24 UTC)返回false,即使2012-11-19 23:54:24 UTC和2012-11-19 23:54:24 UTC之间的差值为空。检查2012-11-19 23:54:24 UTC的实施情况。==。看起来我会像大家建议的那样使用
。)与此匹配器一起,我们最终使用#开始(u of _xxx#来“取整”时间,然后使用Timecop冻结。例如,
now=Time.now.start\u of_hour
然后是
Timecop.freeze(now)do…end
在测试中更改核心类的行为似乎不是一个好主意。实际上,我也遇到了一些问题,因为Rails中出现了各种类似时间的类。我可能会开始将Timecop集成到我的项目中,尽管我怀疑它们也会做类似的事情。哈,我只是在无意中添加了这个链接。最后,我用一种方法来包装Timecop,以此来安排我的时间。