Trouble comparing time with RSpec

Viewed 71965

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 controller action run, but I am in trouble since the spec does not pass. That is, given the following is the spec code:

it "updates updated_at attribute" do
  Timecop.freeze

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

When I run the above spec I get the following error:

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 ==)

How can I make the spec to pass?


Note: I tried also the following (note the utc addition):

it "updates updated_at attribute" do
  Timecop.freeze

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

but the spec still does not pass (note the "got" value difference):

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 ==)
8 Answers

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

Because I was comparing hashes, most of these solutions did not work for me so I found the easiest solution was to simply grab the data from the hash I was comparing. Since the updated_at times are not actually useful for me to test this works fine.

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

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

In Rails 4.1+ you can use Time Helpers:

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
Related