Trouble Comparing Time with Rspec

Trouble comparing time with RSpec

Ruby Time object maintains greater precision than the database does. When the value is read back from the database, it’s only preserved to microsecond precision, while the in-memory representation is precise to nanoseconds.

If you don't care about millisecond difference, you could do a to_s/to_i on both sides of your expectation

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

or

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

Refer to this for more information about why the times are different

Comparing dates with Rspec

ok, do this

expect(FactoryGirl.create(:category_page_with_parsed_results).last_parsed_result.to_date).to eql(Date.parse('2012-01-01'))

RSpec: time comparison doesn't pass

Use 'timecop' gem. This gem handles that for you.

You have a railscast dedicated to this gem: http://railscasts.com/episodes/276-testing-time-web-requests

You could do something like that:

describe 'API Key' do
it 'does expire in 30 days on api scope' do
Timecop.freeze
api_key = FactoryGirl.build(:api_key_session)
expect(api_key.expired_at).to eq(30.days.from_now)
end
end

Adjust it to your needs.

RSpec failing - date comparison

When testing validations don't use expect(object).not_to be_valid or expect(object).to be_valid. This is just a recipe for both false positivies and negatives as you're not actually testing a single behavior - rather you're testing every single validation at once along with your test setup.

RSpec.describe ChainPlan, type: :model do
it 'does not allow an end_time which is after the start_time' do
cp = described_class.new(
start_time: Time.current,
end_time: Time.current - 1.minute
)
cp.valid?
expect(cp.errors.messages_for(:end_time)).to include 'must be after the start date'
end
it "allows a valid start_time/end_time combo" do
cp = described_class.new(
start_time: Time.current,
end_time: Time.current.advance(10.minutes)
)
cp.valid?
expect(cp.errors).to_not have_key :end_date
end
end

Instead setup the object and then call valid? on it to trigger the validations. Write expectations on the errors object to test the actual behavior instead of carpet bombing. For example here you would have completely missed that you where adding the errors to the key :end_date instead of :end_time.

The validation itself also can be improved:

class ChainPlan < ApplicationRecord
belongs_to :faction
has_many :chain_plan_slots
validates :start_time, presence: { on: :create, message: "can't be blank" }
validates :end_time, presence: { on: :create, message: "can't be blank" }
validates :faction, presence: { on: :create, message: "can't be blank" }
validate :end_date_after_start_date?, if: ->{ end_time.present? && start_time.present? }

private

def end_date_after_start_date?
errors.add(:end_time, 'must be after the start date') if end_time < start_time
end
end

Trouble with Rspec for timestamp in hashes

Since you haven't shown how response_body_json nor Presenter#page are defined, I cannot really answer why your current attempt doesn't work.

However, I can instead say that I would use a different approach.

There are two standard ways to write tests like this:

  1. Freeze time.

Assuming you're using a relatively up-to-date rails version, you can use use ActiveSupport::Testing::TimeHelpers#freeze_time somewhere in the test, e.g. something like:

around do |example|
freeze_time { example.run }
end

it 'should return the movie_1 in page format' do
expect(response_body_json).to eql(Presenter.new(ActiveRecordObject).page)
end

If you're on an older rails version, you may need to use travel_to(Time.zone.now) instead.

And if you're on a very old rails version (or a non-rails project!), which doesn't have this helper library, you can use timecop instead.


  1. Use a fuzzy matcher for timestamps (e.g. be_within). Something along the lines of:

.

it 'should return the movie_1 in page format' do
expected_json = Presenter.new(ActiveRecordObject).page
expect(response_body_json).to match(
expected_json.merge(updated_at: be_within(3.seconds).of(Time.zone.now))
)
end

Rails Time inconsistencies with rspec

You should mock the now method of Time to make sure it always match the date in the spec. You never know when a delay will make the spec fail because of some milliseconds. This approach will also make sure that the time on the real code and on the spec are the same.

If you're using the default rspec mock lib, try to do something like:

t = Time.parse("01/01/2010 10:00")
Time.should_receive(:now).and_return(t)

RSpec testing Time objects

You can use RSpec's built-in be_within matcher, which can be also be used for floating point numbers and other data types prone to slight discrepancies:

expect(@model.end_date).to be_within(1.second).of(end_date)


Related Topics



Leave a reply



Submit