Problem setting expectation for test with delayed::job

I’ve got something like this:

post_observer.rb

after_create

…stuff

Delayed::Job.enqueue(PostSharer.new(post, post.user))
end

post_sharer.rb

class PostSharer < Struct.new(:post, user)

def perform
# Delayed::Job calls .perform on the object passed into enqueue
end

end

post_controller_spec.rb

it “shares the post” do

PostSharer.expects(:new).once
lambda { do_post }.should change(Delayed::Job, :count).by(1)

end

This fails due to the expectation put on PostSharer receiving .new —
if I
remove that, then it all works fine… And if I look at the test
database,
Delayed::Job has created a job for PostSharer, so it is all working as
desired… I just wanted to take it a step further and ensure that the
right
class is being instantiated. I am assuming this is because setting an
expectation on new is somehow changing the structure of the class and
confusing
delayed job? Maybe because it’s a struct?

  1. PostsController creating a post sharing shares when it should
    Failure/Error: post :create, { :submit_action => submit_type.to_s,
    :post => new_post(post_attributes).attributes }
    ArgumentError:
    Cannot enqueue items which do not respond to perform

    ./app/observers/post_observer.rb:12:in `after_create’

    ./app/models/post.rb:156:in `set_state_to_open_for_free_requests’

    ./app/controllers/posts_controller.rb:39:in `create’

    ./spec/controllers/post_controller_spec.rb:8:in `do_post’

    ./spec/controllers/post_controller_spec.rb:77

    ./spec/controllers/post_controller_spec.rb:75

Patrick J. Collins
http://collinatorstudios.com

On Dec 1, 2011, at 6:34 PM, Patrick J. Collins wrote:

post_controller_spec.rb

This fails due to the expectation put on PostSharer receiving .new — if I
remove that, then it all works fine… And if I look at the test database,
Delayed::Job has created a job for PostSharer, so it is all working as
desired…

You could automate “if I look at the test database” bit as part of the
test.

I just wanted to take it a step further and ensure that the right
class is being instantiated.

Wouldn’t the record in the database be enough? If not, you could do
this:

Delayed::Job.expects(:enqueue).with(PostSharer.new(post, post.user))

I am assuming this is because setting an
expectation on new is somehow changing the structure of the class and confusing
delayed job? Maybe because it’s a struct?

In rspec-mocks, when you say Klass.stub(:new), Klass.new returns a
proxy, not an instance of the Klass. I’m pretty sure the same is true of
mocha.

Cheers,
David

In rspec-mocks, when you say Klass.stub(:new), Klass.new returns a proxy, not
an instance of the Klass. I’m pretty sure the same is true of mocha.

Ok-- I guess I wasn’t realizing that Klass.expects(:new) was actually
stubbing
the class… I was thinking it was doing something like making a
duplicate of
the initialize method (making it something like “_initialize”) and then
overwriting the real initialize method with something that calls
_initialize
but also sets a flag to indicate that .new had been called… So that’s
apparently not quite the case?

In any event, after reading what you wrote I got my test to pass by
doing:

PostSharer.expects(:new).once.returns stub(“Fake Post Sharer”, :perform
=> true)

So this way delayed_job still can call perform on the fake object and
everyone
is happy (especially me).

Patrick J. Collins
http://collinatorstudios.com