Scenario
Have a race case where concurrency can cause a duplicate key error. Take for example:
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
The Test
Now, to test the race case (based on the answer to Simulating race conditions in RSpec unit tests) we just need to monkey patch before_create_customer_by_external_id
to call create_customer_from_external_id
.
The Question
How can you do this without overriding the whole class and getting a "method not found" error?
A step on from monkey patching the class is to create an 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
This is not so very different to your solution but keeps the monkeypatch isolated to that context block.
You may not need the custom routes block - rspec sets up some dummy routes for the rest methods (edit, show, index etc)
If this context is inside a describe ControllerToOverride
block then the argument to controller is optional, unless you have turned off config.infer_base_class_for_anonymous_controllers