16

In a controller method I set a user's variable activation_sent_at equal to Time.zone.now when an activation email is sent to that user. On the development server this seems to work (although time expressions in my application are 2 hours behind on the local time of my computer).

I want to include an integration test that tests whether activation_sent_at indeed gets set properly. So I included the line:

assert_equal @user.activation_sent_at, Time.zone.now

However, this produces the error:

No visible difference in the ActiveSupport::TimeWithZone#inspect output.
You should look at the implementation of #== on ActiveSupport::TimeWithZone or its members.

I think it's suggesting to use another expression for Time.zone.now in my test. I've looked at different sources, including http://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html, but am not sure what to do here. Any suggestions what could be causing this error?

Additional info: Adding puts Time.zone.now and puts @stakeholder.activation_sent_at confirms the two are equal. Not sure what generates the failure/error.

3 Answers 3

32

The issue is that the 2 dates are very close to each other but not the same. You can use assert_in_delta

assert_in_delta @user.activation_sent_at, Time.zone.now, 1.second

For RSpec, a similar approach would be to use be_within:

expect(@user.activation_sent_at).to be_within(1.second).of Time.zone.now

4

The reason is because Time.now or Time.zone.now include milliseconds (when you do a simple put to print the time it doesn't show milliseconds). However, when you persist the timestamp in the database these milliseconds likely get lost unless the db field is configured to store milliseconds. So when you read the value from the db it will not include milliseconds, hence the times are slightly different.

One solution is to remove milliseconds from Time.now. You can do this like so Time.now.change(usec: 0). This should fix the error in the tests.

3

The problem is that your times are very close but not quite equal. They are probably off by a few fractions of a second.

One solution to issues like this is a testing gem called timecop. It gives you the ability to mock Time.now so that it will temporarily return a specific value that you can use for comparisons.

2
  • 2
    Funny enough, Timecop was the first savior I thought of when I encountered this issue, yet it didn't help. Funny.
    – bbozo
    Commented Nov 6, 2017 at 11:48
  • 1
    The issue is that even if you freeze time, it doesn't help - probably because of some infinitesimal drift that whatever arithmetic is at play can't reliably compare. However, when I set the time to a particular moment of my choosing with the miliseconds rounded, off, then it works, ie Timecop.freeze fails and Timecop.freeze Time.parse("2016-09-13 18:26:09 +0200") works`
    – bbozo
    Commented Nov 6, 2017 at 11:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.