Rspec控制器测试 - 如何存根和/或测试自定义对象

时间:2016-02-11 05:51:43

标签: ruby-on-rails ruby-on-rails-4 rspec controller rspec-rails

下面就过去了!

控制器代码:

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

规范代码:

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

但是如果只是那么容易......我有一些自定义的作业对象在保存后执行,所以代码实际上是这样的:

控制器代码:

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

我很想测试自定义对象是否接收到具有正确参数的方法链,但不确定如何在规范代码中创建这样的东西:

before do
  class RoadrunnerEmailAlert
    def async
    end
  end
end

但那是如此做作,当然不对......建议赞赏!

1 个答案:

答案 0 :(得分:1)

如果这有助于其他人......这是一个非常全面的答案。

上下文&设计说明

  1. 异步框架是Sucker Punch gem (http://brandonhilkert.com/blog/why-i-wrote-the-sucker-punch-gem/)。 那时候,这对我来说是最简单的事情 延迟工作,Sidekick等
  2. 基本上它的工作原理如下:在Controller中引用一个Job然后引用其他任何东西(在我的例子中,是一些PORO)
  3. 如果我真的在严格测试,我想测试一下A)Controller适当调用Job并传递正确的参数,B)Job调用适当的PORO并传递正确的参数。但相反,我只测试了控制器调用适当的PORO并传递正确的参数,即作业已经在工作。
  4. 控制器代码

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

    职位代码

    # 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
    

    测试代码

    这里真正重要的是,我不知道为什么我一直忘记(但仅在测试中)是阶级与课堂实例。对于PORO,由于我实例化了对象,我需要测试2个不同的"层" (首先,对象被适当地实例化,其次是实例化对象被适当地操作)。

    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