简体   繁体   中英

Rspec controller test - how to stub and/or test custom objects

Below is passing!

Controller code:

class OrdersController
  def create
    ...
    @order.save
  end
end

Spec code:

describe OrdersController do
  it "should call save method" do
    Order.any_instance.should_receive(:save)
    post :create
  end
end

But if only it were that easy... I have some custom job objects that are executed after the save, so the code actually looks like this:

Controller code:

class OrdersController
  def create
    ...
    @order.save
    RoadrunnerEmailAlert.new.async.perform(@order.id, true)
    CalendarInvite.new.async.perform(@order.id)
    RoadrunnerTwilioAlert.new.async.perform(@order.id)
  end
end

I would love to test that the custom objects are receiving the chain of methods with the right parameters, but not sure how, short of creating something in the spec code like this:

before do
  class RoadrunnerEmailAlert
    def async
    end
  end
end

But that's so contrived, it certainly isn't right... advice appreciated!

In case this helps other people... this is a very comprehensive answer.

Context & design notes

  1. The async framework is Sucker Punch gem ( http://brandonhilkert.com/blog/why-i-wrote-the-sucker-punch-gem/ ). Back then, this was the easiest thing for me to use after looking at Delayed Job, Sidekick, etc
  2. Basically it works like this: in Controller reference a Job that then references anything else (in my case, some POROs)
  3. If I were really rigidly testing, I'd want to test that A) the Controller calls the Job appropriately and passes the right parameters, and B) the Job calls the appropriate POROs and passes the right parameters. But instead, I just tested that the Controller calls the appropriate POROs and passes the right parameters, ie, the Jobs are already working.

Controller code

@order.save
RoadrunnerEmailAlert.new.async.perform(@order.id, true)
CalendarInvite.new.async.perform(@order.id)
RoadrunnerTwilioAlert.new.async.perform(@order.id)

Job code

# app/jobs/roadrunner_email_alert.rb
class RoadrunnerEmailAlert
  include SuckerPunch::Job
  def perform(order_id, require_tos)
    ActiveRecord::Base.connection_pool.with_connection do
        OrderMailer.success_email(order_id, require_tos).deliver
    end
  end
end

# app/jobs/calendar_invite.rb
class CalendarInvite
  include SuckerPunch::Job
  def perform(order_id)
    ActiveRecord::Base.connection_pool.with_connection do
      CreateCalendar.new(order_id).perform
    end
  end
end

# app/jobs/roadrunner_twilio_alert.rb
class RoadrunnerTwilioAlert
  include SuckerPunch::Job
  def perform(order_id)
    ActiveRecord::Base.connection_pool.with_connection do
        CreateAlert.new(order_id).perform
    end
  end
end

Test code

The really big thing here that I don't know why I keep forgetting (but only in testing) is class vs. instance of class. For the POROs, since I'm instantiating the object, I needed to test 2 different "layers" (first that the object is instantiated appropriately, second that the instantiated object is acted upon appropriately).

require 'sucker_punch/testing/inline'

describe "Controller code" do
  before do
    OrderMailer.any_instance.stub(:success_email)

    mock_calendar = CreateCalendar.new(1)
    CreateCalendar.stub(:new).and_return(mock_calendar)
    CreateCalendar.any_instance.stub(:perform)

    mock_alert = CreateAlert.new(1)
    CreateAlert.stub(:new).and_return(mock_alert)
    CreateAlert.any_instance.stub(:perform)
  end

  it "should call appropriate async jobs" do
    expect_any_instance_of(OrderMailer).to receive(:success_email).with(1, true)

    expect(CreateCalendar).to receive(:new).with(1)
    expect_any_instance_of(CreateCalendar).to receive(:perform)

    expect(CreateAlert).to receive(:new).with(1)
    expect_any_instance_of(CreateAlert).to receive(:perform)

    post :create
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM