diff --git a/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb b/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb index e223dd97..481a386f 100644 --- a/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb +++ b/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb @@ -65,9 +65,13 @@ module Shoulda def simulate_controller_action ensure_action_and_verb_present! - stubbed_model_attributes + stub_model_attributes - context.send(verb, action) + begin + context.send(verb, action) + ensure + unstub_model_attributes + end verify_permit_call end @@ -84,17 +88,26 @@ module Shoulda attributes & @model_attrs.shoulda_permitted_params end - def stubbed_model_attributes + def stub_model_attributes @model_attrs = self.class.stubbed_parameters_class.new(arbitrary_attributes) local_model_attrs = @model_attrs ::ActionController::Parameters.class_eval do + alias_method :'shoulda_original_[]', :[] + define_method :[] do |*args| local_model_attrs end end end + def unstub_model_attributes + ::ActionController::Parameters.class_eval do + alias_method :[], :'shoulda_original_[]' + undef_method :'shoulda_original_[]' + end + end + def ensure_action_and_verb_present! if action.blank? raise ActionNotDefinedError diff --git a/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb b/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb index dc44cced..86a53576 100644 --- a/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +++ b/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb @@ -102,6 +102,35 @@ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do expect { matcher.matches? } .to raise_error(Shoulda::Matchers::ActionController::StrongParametersMatcher::VerbNotDefinedError) end + + context 'Stubbing ActionController::Parameters#[]' do + it "does not permanently stub []" do + controller_for_resource_with_strong_parameters(action: :create) do + params.require(:user).permit(:name) + end + + described_class.new([:name]).in_context(self).for(:create).matches? + + param = ActionController::Parameters.new(name: 'Ralph')[:name] + expect(param.singleton_class).not_to include( + Shoulda::Matchers::ActionController::StrongParametersMatcher::StubbedParameters + ) + end + + it 'prevents permanently overwriting [] on error' do + stub_controller_with_exception + + begin + described_class.new([:name]).in_context(self).for(:create).matches? + rescue SimulatedError + end + + param = ActionController::Parameters.new(name: 'Ralph')[:name] + expect(param.singleton_class).not_to include( + Shoulda::Matchers::ActionController::StrongParametersMatcher::StubbedParameters + ) + end + end end describe "failure message" do @@ -157,4 +186,20 @@ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do end end end + + def stub_controller_with_exception + controller = define_controller('Examples') do + def create + raise SimulatedError + end + end + + setup_rails_controller_test(controller) + + define_routes do + get 'examples', to: 'examples#create' + end + end + + class SimulatedError < StandardError; end end