diff --git a/lib/rspec/core/memoized_helpers.rb b/lib/rspec/core/memoized_helpers.rb index dd7f3c000a..771c12d716 100644 --- a/lib/rspec/core/memoized_helpers.rb +++ b/lib/rspec/core/memoized_helpers.rb @@ -288,7 +288,26 @@ def let(name, &block) raise( "#let or #subject called with a reserved name #initialize" ) if :initialize == name - MemoizedHelpers.module_for(self).__send__(:define_method, name, &block) + our_module = MemoizedHelpers.module_for(self) + + # If we have a module clash in our helper module + # then we need to remove it to prevent a warning. + # + # Note we do not check ancestor modules (see: `instance_methods(false)`) + # as we can override them. + if our_module.instance_methods(false).include?(name) + our_module.__send__(:remove_method, name) + end + our_module.__send__(:define_method, name, &block) + + # If we have a module clash in the example module + # then we need to remove it to prevent a warning. + # + # Note we do not check ancestor modules (see: `instance_methods(false)`) + # as we can override them. + if instance_methods(false).include?(name) + remove_method(name) + end # Apply the memoization. The method has been defined in an ancestor # module so we can use `super` here to get the value. diff --git a/spec/rspec/core/shared_context_spec.rb b/spec/rspec/core/shared_context_spec.rb index dfd11b7b80..67218f2418 100644 --- a/spec/rspec/core/shared_context_spec.rb +++ b/spec/rspec/core/shared_context_spec.rb @@ -66,6 +66,19 @@ expect(group.new.foo).to eq('foo') end + it 'supports overriding let without warnings' do + shared = Module.new do + extend RSpec::SharedContext + let(:foo) { 'foo' } + end + group = RSpec.describe do + include shared + let(:foo) { 'bar' } + end + + expect(group.new.foo).to eq('bar') + end + it "supports let when applied to an individual example via metadata" do shared = Module.new do extend RSpec::SharedContext