programing

RSpec과 시간 비교 문제

oldcodes 2023. 6. 3. 08:43
반응형

RSpec과 시간 비교 문제

저는 Rails 4와 rspec-rails gem 2.14에 Ruby를 사용하고 있습니다.내 목표에 대해 현재 시간을 비교하고 싶습니다.updated_at컨트롤러 작업 실행 후 개체 특성이 발생하지만 사양이 통과되지 않아 문제가 발생했습니다.즉, 다음과 같은 사양 코드가 주어집니다.

it "updates updated_at attribute" do
  Timecop.freeze

  patch :update
  @article.reload
  expect(@article.updated_at).to eq(Time.now)
end

위 사양을 실행하면 다음 오류가 발생합니다.

Failure/Error: expect(@article.updated_at).to eq(Time.now)

   expected: 2013-12-05 14:42:20 UTC
        got: Thu, 05 Dec 2013 08:42:20 CST -06:00

   (compared using ==)

어떻게 하면 스펙을 통과시킬 수 있습니까?


참고: 다음도 시도했습니다(참고:utc추가):

it "updates updated_at attribute" do
  Timecop.freeze

  patch :update
  @article.reload
  expect(@article.updated_at.utc).to eq(Time.now)
end

그러나 사양은 여전히 통과하지 못합니다("got" 값 차이에 유의).

Failure/Error: expect(@article.updated_at.utc).to eq(Time.now)

   expected: 2013-12-05 14:42:20 UTC
        got: 2013-12-05 14:42:20 UTC

   (compared using ==)

를 사용합니다.be_within기본 rspec 일치자가 더 우아합니다.

expect(@article.updated_at.utc).to be_within(1.second).of Time.now

Ruby Time 개체는 데이터베이스보다 더 높은 정밀도를 유지합니다.데이터베이스에서 값을 다시 읽을 때 메모리 내 표현은 나노초로 정밀하게 유지되는 반면 마이크로초의 정밀도만 유지됩니다.

밀리초의 차이가 신경이 쓰이지 않는다면 예상과 달리 to_s/to_i를 모두 수행할 수 있습니다.

expect(@article.updated_at.utc.to_s).to eq(Time.now.to_s)

또는

expect(@article.updated_at.utc.to_i).to eq(Time.now.to_i)

시간이 다른 이유에 대한 자세한 내용은 이 항목을 참조하십시오.

오래된 게시물이지만, 해결책을 위해 이곳에 들어오는 사람들에게 도움이 되길 바랍니다.날짜를 수동으로 만드는 것이 더 쉽고 안정적이라고 생각합니다.

it "updates updated_at attribute" do
  freezed_time = Time.utc(2015, 1, 1, 12, 0, 0) #Put here any time you want
  Timecop.freeze(freezed_time) do
    patch :update
    @article.reload
    expect(@article.updated_at).to eq(freezed_time)
  end
end

이렇게 하면 저장된 날짜가 올바른 날짜임을 확인할 수 있습니다.to_x또는 소수에 대한 걱정.

이 문제를 해결하기 위해 찾은 가장 쉬운 방법은current_time다음과 같은 테스트 도우미 방법:

module SpecHelpers
  # Database time rounds to the nearest millisecond, so for comparison its
  # easiest to use this method instead
  def current_time
    Time.zone.now.change(usec: 0)
  end
end

RSpec.configure do |config|
  config.include SpecHelpers
end

이제 시간은 항상 가장 가까운 밀리초로 반올림되어 비교가 간단합니다.

it "updates updated_at attribute" do
  Timecop.freeze(current_time)

  patch :update
  @article.reload
  expect(@article.updated_at).to eq(current_time)
end

로서.Oin시사하는 바가 있습니다.be_within매칭이 최선의 방법입니다.

...그리고 몇 가지 더 많은 사용 사례가 있습니다 -> http://www.eq8.eu/blogs/27-rspec-be_within-matcher

그러나 이 문제를 해결하는 또 다른 방법은 내장된 레일을 사용하는 것입니다.midday그리고.middnight특성.

it do
  # ...
  stubtime = Time.now.midday
  expect(Time).to receive(:now).and_return(stubtime)

  patch :update 
  expect(@article.reload.updated_at).to eq(stubtime)
  # ...
end

이제 이것은 단지 시연을 위한 것입니다!

당신이 모든 Time.new calls를 스텁하고 있기 때문에 나는 컨트롤러에서 이것을 사용하지 않을 것입니다 => 모든 시간 속성은 동일한 시간을 가질 것입니다 => 당신이 달성하려고 하는 개념을 증명하지 못할 수도 있습니다.저는 주로 다음과 같은 구성된 루비 오브젝트에서 사용합니다.

class MyService
  attr_reader :time_evaluator, resource

  def initialize(resource:, time_evaluator: ->{Time.now})
    @time_evaluator = time_evaluator
    @resource = resource
  end

  def call
    # do some complex logic
    resource.published_at = time_evaluator.call
  end
end

require 'rspec'
require 'active_support/time'
require 'ostruct'

RSpec.describe MyService do
  let(:service) { described_class.new(resource: resource, time_evaluator: -> { Time.now.midday } ) }
  let(:resource) { OpenStruct.new }

  it do
    service.call
    expect(resource.published_at).to eq(Time.now.midday)    
  end
end

하지만 솔직히 저는 계속하는 것을 추천합니다.be_withinTime.now.midday를 비교할 때 조차 일치합니다!

그래서 네, 계속해 주세요.be_within매치처 ;)


2017-02 업데이트

댓글 질문:

일부 hash_1 값이 pre-db-times이고 hash_2의 해당 값이 post-db-times일 때 시간이 해시에 있으면 어떻게 합니까?

expect({mytime: Time.now}).to match({mytime: be_within(3.seconds).of(Time.now)}) `

을 RSpect 파일에 전달할 수 .matchmatcher(예: 순수 RSpec으로도 API 테스트를 수행할 수 있음)

"post-db-times"는 DB에 저장 후 생성되는 문자열을 의미하는 것 같습니다.저는 이 사례를 두 가지 예상으로 분리할 것을 제안합니다(하나는 해시 구조를 보장하고, 다른 하나는 시간을 확인하는 것).따라서 다음과 같은 작업을 수행할 수 있습니다.

hash = {mytime: Time.now.to_s(:db)}
expect(hash).to match({mytime: be_kind_of(String))
expect(Time.parse(hash.fetch(:mytime))).to be_within(3.seconds).of(Time.now)

그러나 이 사례가 테스트 제품군에서 너무 자주 발생하는 경우에는 자신만의 RSpec 매칭기를 작성하는 것)를 작성하는 것이 좋습니다.be_near_time_now_db_string) 한 후 를 ) db 자열시간을변로환다한음다사의 match(hash):

 expect(hash).to match({mytime: be_near_time_now_db_string})  # you need to write your own matcher for this to work.

할 수 .to_s(:db).

expect(@article.updated_at.to_s(:db)).to eq '2015-01-01 00:00:00'
expect(@article.updated_at.to_s(:db)).to eq Time.current.to_s(:db)

해시를 비교하는 과정에서 이러한 솔루션의 대부분이 제게 적합하지 않았기 때문에 비교하는 해시에서 데이터를 가져오는 것이 가장 쉬운 솔루션이라는 것을 알게 되었습니다.updated_ats 시간은 실제로 제가 이 작업을 테스트하는 데 유용하지 않기 때문에 잘 작동합니다.

data = { updated_at: Date.new(2019, 1, 1,), some_other_keys: ...}

expect(data).to eq(
  {updated_at: data[:updated_at], some_other_keys: ...}
)

Rails 4.1+에서는 시간 도우미를 사용할 수 있습니다.

include ActiveSupport::Testing::TimeHelpers

describe "some test" do
  around { |example| freeze_time { example.run } }

  it "updates updated_at attribute" do
    expect { patch :update }.to change { @article.reload.updated_at }.to(Time.current)
  end
end

언급URL : https://stackoverflow.com/questions/20403063/trouble-comparing-time-with-rspec

반응형