Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails/Rspec: Testing delayed_job mails

Just wondering how to test that actionmailer requests are actually sent to the delayed_job que in rspec.

I would have assumed it was quite simple, but my delayed_job queue doesn't seem to be incrementing. Code below:

Controller:

  def create
    @contact = Contact.new(params[:contact])
      if @contact.save
        contactmailer = ContactMailer
        contactmailer.delay.contact_message(@contact)
        redirect_to(contacts_url)
      else
        render :action => "new"
      end

Spec:

  it "queues mail when a contact is created" do
    expectedcount = Delayed::Job.count + 1
    Contact.stub(:new).with(mock_contact()) { mock_contact(:save => true) }
    post :create, :contact => mock_contact
    expectedcount.should eq(Delayed::Job.count)
  end

Both before and after the call to the controller, the Delayed::Job.count returns 0. I've tried taking the conditional out of the controller, but I still can't get the delayed job count to increment.

Any suggestions appreciated - cheer

like image 941
PlankTon Avatar asked Jun 15 '11 02:06

PlankTon


3 Answers

You can also test what the jobs will do by running them or turning off queuing.

Tweak config whenever you want (i.e. in a before :each block).

Delayed::Worker.delay_jobs = false

or perform your saved jobs

Delayed::Worker.new.work_off.should == [1, 0]

I have been using this method happily for a while. For one thing, using the new any_instance support in RSpec, you can test your delayed methods effects directly. However, I've found tests that use work_off to be slow.

What I usually do now is:

mock_delay = double('mock_delay').as_null_object
MyClass.any_instance.stub(:delay).and_return(mock_delay)
mock_delay.should_receive(:my_delayed_method)

Then I have a separate spec for my_delayed_method. This is much faster, and probably better unit testing practice -- particularly for controllers. Though if you're doing request specs or other integration-level specs, then you probably still want to use work_off.

like image 100
John Hinnegan Avatar answered Dec 06 '22 23:12

John Hinnegan


I think your mock object is somehow introducing an error -- it's hard to tell exactly how without seeing the definition of the mock_contact method.

In any case, you might try something along these lines:

  it "queues mail when a contact is created" do
    Contact.stub(:new) { mock_model(Contact,:save => true) }
    Delayed::Job.count.should == 0
    post :create, {}
    Delayed::Job.count.should == 1
  end

or the sexier version (caveat: I always end up doing it the non-sexy way):

  it "queues mail when a contact is created" do
    Contact.stub(:new) { mock_model(Contact,:save => true) }
    expect {
      post :create, {}
    }.to change(Delayed::Job.count).by(1)
  end
like image 22
zetetic Avatar answered Dec 07 '22 00:12

zetetic


You can also follow the convention (from Railscast 275) of

    ActionMailer::Base.deliveries.last.to.should == user.email

but instead do this:

    Delayed::Job.last.handler.should have_content(user.email)
like image 43
ajbraus Avatar answered Dec 06 '22 23:12

ajbraus