RSpec과의 시간 비교 문제
Ruby on Rails 4와 rspec-rails gem 2.14를 사용하고 있습니다. 내 객체의 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 ==)
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)
시간이 다른 이유에 대한 자세한 내용은 이것을 참조하십시오
be_within
기본 rspec 매처를 사용하는 것이 더 우아 하다는 것을 알았습니다 .
expect(@article.updated_at.utc).to be_within(1.second).of Time.now
오래된 게시물이지만 해결책을 찾기 위해 여기에 들어온 사람에게 도움이되기를 바랍니다. 날짜를 수동으로 만드는 것이 더 쉽고 안정적이라고 생각합니다.
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)
patch :update
@article.reload
expect(@article.updated_at).to eq(freezed_time)
end
이렇게하면 저장된 날짜가 to_x
소수점에 대한 걱정이나 걱정 없이 올바른 날짜가됩니다 .
를 사용하여 데이터베이스에 저장된 날짜 / 날짜 / 시간 객체를 문자열로 변환 할 수 있습니다 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)
그렇습니다. 매 처가 Oin
제안 be_within
하는 것이 가장 좋습니다.
... 더 많은 uscase가 있습니다-> http://www.eq8.eu/blogs/27-rspec-be_within-matcher
But one more way how to deal with this is to use Rails built in midday
and middnight
attributes.
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
Now this is just for demonstration !
I wouldn't use this in a controller as you are stubbing all Time.new calls => all time attributes will have same time => may not prove concept you are trying to achive. I usually use it in composed Ruby Objects similar to this:
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
But honestly I recommend to stick with be_within
matcher even when comparing Time.now.midday !
So yes pls stick with be_within
matcher ;)
update 2017-02
Question in comment:
what if the times are in a Hash? any way to make expect(hash_1).to eq(hash_2) work when some hash_1 values are pre-db-times and the corresponding values in hash_2 are post-db-times? –
expect({mytime: Time.now}).to match({mytime: be_within(3.seconds).of(Time.now)}) `
you can pass any RSpec matcher to the match
matcher (so e.g. you can even do API testing with pure RSpec)
As for "post-db-times" I guess you mean string that is generated after saving to DB. I would suggest decouple this case to 2 expectations (one ensuring hash structure, second checking the time) So you can do something like:
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)
But if this case is too often in your test suite I would suggest writing your own RSpec matcher (e.g. be_near_time_now_db_string
) converting db string time to Time object and then use this as a part of the match(hash)
:
expect(hash).to match({mytime: be_near_time_now_db_string}) # you need to write your own matcher for this to work.
The easiest way I found around this problem is to create a current_time
test helper method like so:
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
Now the time is always rounded to the nearest millisecond to comparisons are straightforward:
it "updates updated_at attribute" do
Timecop.freeze(current_time)
patch :update
@article.reload
expect(@article.updated_at).to eq(current_time)
end
참고URL : https://stackoverflow.com/questions/20403063/trouble-comparing-time-with-rspec
'Programming' 카테고리의 다른 글
Google 보이지 않는 reCAPTCHA 배지를 숨기는 방법 (0) | 2020.07.29 |
---|---|
sed를 사용하여 문자열에서 첫 번째 X 문자를 제거하려면 어떻게합니까? (0) | 2020.07.29 |
iOS로 이미지 크기를 쉽게 조정하고 최적화하는 방법은 무엇입니까? (0) | 2020.07.29 |
PHP 용으로 예쁜 글씨가 있습니까? (0) | 2020.07.29 |
C #에서 사용자 지정 특성을 만드는 방법 (0) | 2020.07.29 |