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

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
tatyree (Guest)
on 2009-01-29 20:40
(Received via mailing list)
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
David C. (Guest)
on 2009-01-29 20:46
(Received via mailing list)
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.
Aslak H. (Guest)
on 2009-01-29 20:46
(Received via mailing list)
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
Nick H. (Guest)
on 2009-01-29 21:25
(Received via mailing list)
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
Pat M. (Guest)
on 2009-01-30 05:33
(Received via mailing list)
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
tatyree (Guest)
on 2009-01-30 11:54
(Received via mailing list)
Thanks for that everyone! Henceforth I shall always endeavour to stop
the flow of time in all of my specs!
Mark W. (Guest)
on 2009-01-30 17:37
(Received via mailing list)
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
David C. (Guest)
on 2009-01-30 18:04
(Received via mailing list)
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/...
Mark W. (Guest)
on 2009-01-30 19:17
(Received via mailing list)
On Fri, Jan 30, 2009 at 7:38 AM, David C.
<removed_email_address@domain.invalid>wrote:

> 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/...
>

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
Pat M. (Guest)
on 2009-01-30 20:30
(Received via mailing list)
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/...
>
> 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
This topic is locked and can not be replied to.