你怎么能在rspec中修补一个控制器?

时间:2015-03-26 07:19:29

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

方案

有一个竞争案例,其中并发可能导致重复键错误。举个例子:

def before_create_customer_by_external_id
end

def create_customer_from_external_id(external_id = nil)
  @customer = current_account.customers.create!(external_id: external_id || @external_id)
end

def set_new_or_old_customer_by_external_id
  if @customer.blank?
    before_create_customer_by_external_id
    create_customer_from_external_id
  end
rescue ActiveRecord::RecordInvalid => e
  raise e unless Customer.external_id_exception?(e)
  @customer = current_account.customers.find_by_external_id(@external_id)
end

测试

现在,为了测试竞赛案例(根据Simulating race conditions in RSpec unit tests的答案),我们只需要修补before_create_customer_by_external_id来调用create_customer_from_external_id

问题

如果不覆盖整个班级并获得“找不到方法”错误,怎么能这样做呢?

2 个答案:

答案 0 :(得分:2)

经过一番挖掘后,我想出了以下解决方案:

context 'with race condition' do
  it 'should hit race case and do what is expected' do
    ControllerToOverride.class_eval do
      def before_create_new_customer_by_external_id
        create_customer_from_external_id
      end
    end

    # ...expect...
  end
end

我通过使用代码覆盖率工具和调试语句验证了它正在遇到竞争案例。

很高兴知道这里是否有更清洁的方式。

答案 1 :(得分:1)

猴子修补课程的一步是创建一个anonymous subclass

context "with race condition" do
   controller(ControllerToOverride) do
      def before_create_customer_by_external_id
      end
   end

   it "should deal with it " do
     routes.draw { # define routes here }
     ...
   end
end

这与您的解决方案没有太大的不同,但是将monkeypatch与该上下文块隔离开来。

您可能不需要自定义路由块 - rspec为其余方法设置一些虚拟路由(编辑,显示,索引等)

如果此上下文位于describe ControllerToOverride块内,则控制器的参数是可选的,除非您已关闭config.infer_base_class_for_anonymous_controllers