Strange message expectation behaviour with Time.now in a find condition


#1

Even if this is a bug, it’s a pretty obscure one. It was frustrating
the hell out of me until I found a workaround, so I thought I’d just
post the details:

Given a find like this:

def self.find_old
User.all(:conditions => [‘updated_at <= ?’,Time.now - 1.day)

Trying to set a message expectation will always fail:

it “should find users” do
User.should_receive(:all).with({:conditions => [‘updated_at
<= ?’,Time.now - 1.day]})
User.find_old

with the error:
Mock ‘Class’ expected :all with ({:conditions=>[“updated_at <= ?”,
Wed Jan 28 17:59:02 +0000 2009]}) but received it with ({:conditions=>
[“updated_at <= ?”, Wed Jan 28 17:59:02 +0000 2009]})

Expected and received here are identical.

The only workaround I’ve found (the example here is simplified, but
the datetime in the model where I discovered the bug is critical and
so needs to be spec’d) is to wrap the Time call and the expectation in
another format:

User.all(:conditions => [‘updated_at <= ?’,(Time.now -
1.day).xmlschema])

and

it “should find users” do
User.should_receive(:all).with({:conditions => [‘updated_at <= ?’,
(Time.now - 1.day).xmlschema]})

I’m on rspec 1.1.12 on rails 2.1.0 and I’m guessing the + in the
formatted datetime is playing hell with a regexp somewhere.
Unfortunately, I don’t have time to dig in to it myself right now.

I was seeing the same behaviour when I tried hash_including
(:conditions => [‘updated_at <= ?’,Time.now - 1.day]) as part of the
expectation.

Thanks,
Todd


#2

On Thu, Jan 29, 2009 at 12:08 PM, tatyree removed_email_address@domain.invalid wrote:

Trying to set a message expectation will always fail:
[“updated_at <= ?”, Wed Jan 28 17:59:02 +0000 2009]})

I was seeing the same behaviour when I tried hash_including
(:conditions => [‘updated_at <= ?’,Time.now - 1.day]) as part of the
expectation.

The problem is a display problem - the Time objects are actually
different
by some milliseconds, but you don’t see that in the feedback.

Try this:

now = Time.now
Time.stub!(:now).and_return(now)

Then both calls will return the same object.


#3

On Thu, Jan 29, 2009 at 7:08 PM, tatyree removed_email_address@domain.invalid wrote:

Trying to set a message expectation will always fail:
[“updated_at <= ?”, Wed Jan 28 17:59:02 +0000 2009]})

Expected and received here are identical.

No they are not. They are milliseconds apart.

You should always stub Time.now in tests.

Aslak


#4

On 29/01/2009, at 1:41 PM, David C. wrote:

Wed Jan 28 17:59:02 +0000 2009]}) but received it with ({:conditions=>
1.day).xmlschema])

now = Time.now
Time.stub!(:now).and_return(now)

Then both calls will return the same object.

You’re too fast, David and Aslak! =P

Todd, I fell prey to the same problem a few months ago. As David and
Aslak suggested, just stub out Time#now . Eg:

it ‘should do something’ do
now = Time.now
Time.stub!(:now).and_return now
User.should_receive(:all).with(:conditions => [‘updated_at <= ?’,
now - 1.day])
User.all(:conditions => [‘updated_at <= ?’, Time.now - 1.day])
end

Cheers,
Nick


#5

On Thu, Jan 29, 2009 at 10:41 AM, David C. removed_email_address@domain.invalid
wrote:

def self.find_old

another format:
I’m on rspec 1.1.12 on rails 2.1.0 and I’m guessing the + in the
now = Time.now
Time.stub!(:now).and_return(now)
Then both calls will return the same object.

What David, Aslak & Nick said.

btw…

~:$ irb

Time.now == Time.now
=> false

Pat


#6

Thanks for that everyone! Henceforth I shall always endeavour to stop
the flow of time in all of my specs!


#7

Avdi G. wrote a blog post about why he doesn’t stub Time.now and
instead
always injects a clock into his objects. I disagreed, but I can’t seem
to
find the article now.

///ark


#8

On Fri, Jan 30, 2009 at 9:17 AM, Mark W. removed_email_address@domain.invalid wrote:

Avdi G. wrote a blog post about why he doesn’t stub Time.now and instead
always injects a clock into his objects. I disagreed, but I can’t seem to
find the article now.

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/297660


#9

On Fri, Jan 30, 2009 at 7:38 AM, David C.
removed_email_address@domain.invalidwrote:

On Fri, Jan 30, 2009 at 9:17 AM, Mark W. removed_email_address@domain.invalid wrote:

Avdi G. wrote a blog post about why he doesn’t stub Time.now and
instead
always injects a clock into his objects. I disagreed, but I can’t seem to
find the article now.

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/297660

Yeah, I saw that, but he laid out his case in greater depth in a blog
post
(i know because I posted a comment disagreeing with it!).

The interesting thing is that Avdi says that stubbing Time.now has
broken
some of his tests in the past because of other code (like RSpec) that
calls
it. I can’t say I’ve ever run into that myself, but it bears keeping in
mind.

///ark


#10

On Fri, Jan 30, 2009 at 9:05 AM, Mark W. removed_email_address@domain.invalid wrote:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/297660

Yeah, I saw that, but he laid out his case in greater depth in a blog post
(i know because I posted a comment disagreeing with it!).

The interesting thing is that Avdi says that stubbing Time.now has broken
some of his tests in the past because of other code (like RSpec) that calls
it. I can’t say I’ve ever run into that myself, but it bears keeping in
mind.

I think that using a custom Clock class can be very useful. However,
it can cause confusion in Rails apps, because AR uses Time.now for
created_at and updated_at. You’d have to keep specific track of when
you meant to use Clock and when you wanted to control Time, and to me
it’s just not worth it.

Pat