1

我的 Rails 应用程序中有一组序列化程序对象的单元测试。这些序列化器对象使用google-protobuf (~> 3.5)gem,包括Google::Protobuf::Timestamp对象。对于与时间相关的属性(如purchase_order#created_atline_item#created_atinspection_event#event_occured_at),我们使用 TimeSerializer 对象,实现如下:

# frozen_string_literal: true

module ProtoSerializers
  class TimeSerializer < BaseProtoSerializer
    def serialize
      return if object.nil?

      GOOGLE_BASE::Timestamp.new(seconds: object&.to_i, nanos: object&.nsec)
    end
  end
end

这是通过调用实例化的ProtoSerializers::TimeSerializer.serialize(time),其中time是 Rails Time 或 DateTime 对象。

测试将序列化的预期结果与实际结果进行比较,如果结果匹配则通过,否则失败:

describe '#serialize an inspection whose purchase order and line item are both archived' do

  subject { described_class.serialize(object) }

  let(:purchase_order) { create(:purchase_order, :is_archived) }
  let(:line_item) { create(:line_item, :archived, purchase_order: purchase_order) }
  let(:object) { create(:inspection, line_item: line_item) }

  it 'serializes attributes' do
    expect(subject).to be_a(MyCorp::Proto::MyApp::InspectionEvent)

    expect(subject).to have_attributes(
      ...(misc key-value pairs)...
      purchase_order: ProtoSerializers::PurchaseOrderSerializer.serialize(purchase_order),
      line_item: ProtoSerializers::LineItemSerializer.serialize(line_item),
      event_occurred_at: ProtoSerializers::TimeSerializer.serialize(object.event_occurred_at)
    )
  end
end

根据标准 Rails 实践,PurchaseOrderLineItem模型都具有created_at属性。

这个测试在我的机器上通过,但是当我将它推送到 Github 时失败(它启动了 Jenkins 测试管道)。预期与实际的差异如下所示:

20:00:39        -:line_item => <MyCorp::Proto::MyApp::LineItem: ..., created_at: <Google::Protobuf::Timestamp: seconds: 1522368034, nanos: 909710602>, ...>,
20:00:39        +:line_item => <MyCorp::Proto::MyApp::LineItem: ..., created_at: <Google::Protobuf::Timestamp: seconds: 1522368034, nanos: 909710000>, ...>,
20:00:39        -:purchase_order => <MyCorp::Proto::MyApp::PurchaseOrder: ..., created_at: <Google::Protobuf::Timestamp: seconds: 1522368034, nanos: 909710602>>,
20:00:39        +:purchase_order => <MyCorp::Proto::MyApp::PurchaseOrder: ..., created_at: <Google::Protobuf::Timestamp: seconds: 1522368034, nanos: 909710000>>,

如您所见,seconds属性匹配,但属性nanos偏离了几百纳秒。我尝试在此测试中使用 Timecop,如下所示,但失败的测试仍然存在:

before { Timecop.freeze(Time.now) }

after { Timecop.return }

我不确定 Jenkins 管道和我的机器有什么不同。我正在使用带有 Intel Core i7 处理器的 Macbook,我相信它是 64 位的。

4

1 回答 1

0

似乎原因是在我的机器(配备 Intel Core i7 处理器的 Macbook)上将 64 位整数转换为 Protobufs 32 位整数纳秒时精度损失。为了解决这个问题,我不得不将时间模拟为不会导致精度损失的东西。最后我使用了Epoch时间,如下:

before { Timecop.freeze(Time.at(0)) }

after { Timecop.return }

这解决了这个问题。

于 2018-03-30T17:13:32.650 回答