我在Rails 3测试套件中进行了一个测试,该测试产生了一些断言,用于比较在本地计算机上传递但在我们的CI管道中失败的时间戳。该测试将时间戳存储在Postgres数据库的timestamp字段中,精度为6,并将存储的值与原始时间戳进行比较,非常类似于以下示例:
tmp_time = Time.zone.now
u = User.find(1)
u.updated_at = tmp_time
u.save!
u.reload
assert_equal u.updated_at.to_i, tmp_time.to_i # passes...
assert_equal u.updated_at, tmp_time # fails...
assert_equal u.updated_at.to_f, tmp_time.to_f # fails...
我认为问题在于Ruby的时间表示比存储的值具有更高的精度。
除了比较中不那么精确之外,补偿由于精度导致的值的微小差异的最佳方法是什么?我们已经考虑了覆盖Time.zone.now,但是认为这将导致下游问题。
谢谢。
答案 0 :(得分:2)
我建议对此类测试使用[Timecop] [1]。我还要补充一点,手动触摸时间戳是不好的做法,因此我什至不确定自己要测试什么。听起来您不信任时间戳。但是,如果必须的话,在这里:
Timecop.freeze
tmp_time = Time.zone.now
u = User.find(1)
u.updated_at = tmp_time
u.save!
u.reload
#your tests should pass now
但是,如果不这样做,[请参见核心导轨上的此公开问题] [2]。 如果这与您的问题有关,Timecop应该防止它发生。但是,如果没有,那么恭喜您,您已经找到了一个核心的导轨边缘盒。
答案 1 :(得分:1)
问题可能与数据库的精度无关,而是从定义tmp_time
到保存之间只有一小段时间。
您会看到.to_f
的时间表示立即改变:
irb(main):011:0> 2.times.map { Time.now.to_f }
=> [1551755487.5737898, 1551755487.573792]
使用.to_i
时,通常看不到这种差异,因为它会四舍五入到最接近的秒数。
您可以使用Timecop(如另一个答案所述)来解决此问题:
irb(main):013:0> Timecop.freeze { 2.times.map { Time.now.to_f } }
=> [1551755580.12368, 1551755580.12368]