"lambda Should Change" Behavior Failing When Checking Time

Hello,

I’m having a problem with RSpec when using the “lambda should change
behavior”.

This is happening in a Rails 2.3.4 app and I’m running the following
in my test environment:

‘cucumber’, :version => ‘0.4.2’
‘faker’, :version => ‘0.3.1’
‘notahat-machinist’, :lib => ‘machinist’, :version => ‘1.0.3’
“remarkable_rails”, :lib => false
‘rspec’, :lib => false, :version => ‘1.2.9’
‘rspec-rails’, :lib => false, :version => ‘1.2.9’
‘jtrupiano-timecop’, :lib => ‘timecop’, :version => ‘0.3.0’
‘webrat’, :lib => false, :version => ‘0.5.1’
‘fakeweb’, :lib => false, :version => ‘1.2.6’

In my application, I have a Post model that is using Rubyist’s AASM
gem. The aasm column is defined as ‘state’. The other column of note
is ‘published_at’; this is defined as a datetime.

These are the settings for the state machine:

aasm_column :state
aasm_initial_state :draft
aasm_state :draft
aasm_state :published, :enter => :set_published
aasm_event :publish do
transitions :to => :published, :from => :draft
end

set_published is a method that is defined as:

def set_published
self.update_attributes(:published_at => Time.now)
end

For those that are unfamiliar with Rubyist’s AASM, defining an
aasm_event gives you an bang instance method with the same name of the
aasm_event. For example, I have :publish defined as an aasm_event and
because of this I can call the publish! method on an instance of a
Post. This will change the state from ‘draft’ to ‘published’. It will
also call the set_published method as defined by the :enter statement.

This is my first spec attempt. I’ve removed all of the post attributes
for brevity.

describe “AASM States” do
before(:each) do
@post = Post.create([snip…post attributes here])
end

it “should set the publish date to now when transitioning to
published” do
lambda { @post.publish! }.should change(@post,
:published_at).from(nil).to(Time.now)
end
end

This fails with a message like the following:

published_at should have been changed to Sat Nov 07 15:02:00 -0800
2009, but is now Sat Nov 07 15:02:00 -0800 2009

blink blink

They appear to be the same.

Just in case the time was being altered by milliseconds that I
couldn’t see, I tried using jtrupiano’s Timecop gem to freeze time and
check against the frozen time.

it "should set the publish date to now when transitioning to 

published" do
time = Time.now
Timecop.freeze(time)
lambda { @post.publish! }.should change(@post,
:published_at).from(nil).to(time)
Timecop.return
end

This still gives me the same failure message. For those unfamiliar
with Timecop, here is how it works (in the console):

require ‘Timecop’
=> []

time = Timecop.freeze(Time.now)
=> Sat Nov 07 15:07:32 -0800 2009

sleep(10)
=> 10

time == Time.now
=> true

Timecop.return
=> Sat Nov 07 15:08:09 -0800 2009

time == Time.now
=> false

In development, I know that the published_at time is truly
transitioning from nil to an actual time, I just don’t know why it’s
failing in the spec and even stranger when RSpec tells me that they
are the (supposedly) the same.

From the development console:

p = Post.new([snip…post attributes here])
p.save
=> true

p.published_at
=> nil

p.publish!
=> true

p.published_at
=> Sat Nov 07 15:10:22 -0800 2009

Is there something that I’m missing?

Thank you in advance for your help,

Carlos

On Sat, Nov 7, 2009 at 6:18 PM, Carlos R. [email protected]
wrote:

‘notahat-machinist’, :lib => ‘machinist’, :version => ‘1.0.3’

set_published is a method that is defined as:
also call the set_published method as defined by the :enter statement.
lambda { @post.publish! }.should change(@post,

 lambda { @post.publish! }.should change(@post,

=> Sat Nov 07 15:07:32 -0800 2009
transitioning from nil to an actual time, I just don’t know why it’s

p.publish!
=> true
p.published_at
=> Sat Nov 07 15:10:22 -0800 2009

Is there something that I’m missing?

Thank you in advance for your help,

Hi Carlos,

I would definitely assume that the times are off by milliseconds here
and
that the to_s method simply produces the same result on two different
times.

Not sure why it’s still failing even when using Timecop, though. Perhaps
somebody else has some ideas about that.

HTH,
David

David C. escreveu:

In my application, I have a Post model that is using Rubyist's AASM
   transitions :to => :published, :from => :draft
aasm_event. For example, I have :publish defined as an aasm_event and
 end
published_at should have been changed to Sat Nov 07 15:02:00 -0800
   it "should set the publish date to now when transitioning to

>> time == Time.now
>> p.save
Thank you in advance for your help,

Hi Carlos,

I would definitely assume that the times are off by milliseconds here
and that the to_s method simply produces the same result on two
different times.

Not sure why it’s still failing even when using Timecop, though.
Perhaps somebody else has some ideas about that.

I believe Timecop doesn’t help in this case.

ActiveRecord will probably fill the time using SQL now() instead of
Time.now.

Anyway, I wouldn’t bother to test if the time was changed to now. I
think it suffices to test that time was changed from nil.

If you really want to test that it changed to now, I would write
something like:

@post.published_at.should be_nil
@post.publish!
(Time.now - @post.published_at).should have_at_most(1).second

Or you could write a new matcher if you need to check this often… But
I think this is an already tested ActiveRecord behavior and that you
should test only your code and rely on ActiveRecord to fill the
corrected timestamp.

Good luck,

Rodrigo.


Faça ligações para outros computadores com o novo Yahoo! Messenger

On Nov 08, 2009, at 10:39 am, Rodrigo Rosenfeld R. wrote:

@post.published_at.should be_nil
@post.publish!
(Time.now - @post.published_at).should have_at_most(1).second

FWIW, this is what I do too, although I normally use “should <” or
“be_close”, but the idea is the same. You only need to worry about
more accuracy than that if it’s time-critical.

Also, I’ve found meddling with Time breaks RSpec’s timing - don’t know
if that’s still the case. For times when I really care about timing,
I’ve made a Clock class, and have everything use that, instead of Time
directly.

Ashley


http://www.patchspace.co.uk/
http://www.linkedin.com/in/ashleymoran