rspec/rspec-rails

Private Method Conflict Between Helpers Causes Incorrect Method Call

Closed this issue · 6 comments

What Ruby, Rails and RSpec versions are you using?

Ruby version: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux-gnu]
Rails version: 7.1.3.4
RSpec version: 3.13

  • rspec-core 3.13.0
  • rspec-expectations 3.13.1
  • rspec-mocks 3.13.1
  • rspec-rails 6.1.3
  • rspec-support 3.13.1

Observed behaviour

If two helpers share the name of a private method, the tests will call the last one in alphabetical order.

Expected behaviour

Each Helper should maintain its own methods.

Can you provide an example reproduction?

  1. Create a Helper:
# frozen_string_literal: true

module HelperOneHelper
  def super_def
    private_def
  end

  private

  def private_def 
    "HelperOne"
  end
  
end

  1. Create anotherone sharing the name of the private method
# frozen_string_literal: true

module HelperTwoHelper
  def super_def_two
    private_def
  end

  private

  def private_def 
    "HelperTwo"
  end
 
end

  1. Create the test for the first Helper
# spec/helpers/helper_one_helper_spec.rb
require 'rails_helper'

RSpec.describe HelperOneHelper, type: :helper do
  describe '#super_def' do
    it 'calls the private method and returns the expected string' do
      expect(helper.super_def).to eq("HelperOne")
    end
  end
end

You should get
image

Example

clone https://github.com/OsvaldoGDelRio/helper-failure
run rspec

As far as I can see here is the origin of this:

/var/lib/gems/3.1.0/gems/rspec-rails-6.1.3/lib/rspec/rails/example/helper_example_group.rb

def helper
  _view.tap do |v|
    v.extend(ApplicationHelper) if defined?(ApplicationHelper)
    v.assign(view_assigns)
  end
end
pirj commented

How would it work in the controller if you include both?

How would it work in the controller if you include both?

The same way, it uses the method from the last Helper, in this case Helper two. I guess Rspec is just following the same behavior. Surprisingly, I guess we should give different names for the private methods or encapsulate them into another module.

👋 Method conflicts like this will cause this behaviour, its not something specific to RSpec except that we include both the helper module under test and ApplicationHelper, this is by design.

pirj commented

The only case I can think of is when you include helper A in controller X, and helper B in controller Y.
If A & B’s overlap, this will work in controllers, but not in specs.
Or am I talking nonsense?

Is this something worth fixing?

If they work in controllers they will work in specs, all we do is essentially:

class ThisTestsHelper
  include ApplicationHelper
  include ModuleUnderTest
end

So these must be in ApplicationHelper and must overlap.