Trouble comparing time with RSpec

后端 未结 7 754
北荒
北荒 2020-12-12 15:23

I am using Ruby on Rails 4 and the rspec-rails gem 2.14. For a my object I would like to compare the current time with the updated_at object attribute after a c

7条回答
  •  暗喜
    暗喜 (楼主)
    2020-12-12 16:02

    yep as Oin is suggesting be_within matcher is the best practice

    ...and it has some more uscases -> 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.
    

提交回复
热议问题