Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In a Rails engine Is it possible for Rspec to make use of Rspec support system helpers from another engine?

Given a Rails engine_one that has a spec support file engine_one/spec/support/system/order_functions.rb, containing functionality to support the testing of various order system tests such as simulating a logged in user, adding products to an order etc and contains methods such as log_visitor_in that get used extensively when testing order processing etc...

So now in engine_two that extends some ordering functionality from engine_one I wish to add a new system test that first has to log a visitor in. So how can I make use of that support method from from engine_one?

So far I have mounted the engines in the dummy app I have required engine_one in engine_two/lib/engine.rb I have required the support file in the relevant test but it can't be found and obviously I have added engine_one to engine_two.gemspec

engine_two/spec/rails_helper.rb

require 'engine_one' # and any other gems you need

engine_two/lib/engine_two/engine.rb

require 'engine_one'

in the relevant system test I have the following

engine_two/spec/system/new_payment_methods_spec.rb

require 'rails_helper'
include EngineOne::System

    RSpec.describe "order_payment_feature", type: :system do
      before do
        driven_by(:rack_test)
      end
    
      it "has order payment options" do
        log_visitor_in
      end
    end

This results in the following error

Failure/Error: include EngineOne::System

NameError:
  uninitialized constant EngineOne::System
  Did you mean?  SystemExit

And the helper

module System
  def log_visitor_in()
    administrator = create(:visitor)
    visit ccs_cms.login_url
    fill_in 'login_name', with: visitor.login_name
    fill_in 'Password', with: visitor.password
    click_button 'Login'
  end

end

I have tried with a require instead of an include but that results in a file not found error Plus I have tried changing the include path to

include EngineOne::Spec::Support::System resulting in the same error

So I guess I'm looking for the correct path but I am stuck or missing some other way to include the helper. These are Rails 7 engines.

like image 605
jamesc Avatar asked Sep 01 '25 16:09

jamesc


1 Answers

When you require a file, ruby searches for it relative to paths in $LOAD_PATH; spec/ or test/ are not part of it.

app directory is a special one in rails, any subdirectory automatically becomes part of autoload_paths. Auto load paths can be seen here ActiveSupport::Dependencies.autoload_paths.

Any classes/modules defined inside app/* directories can be used without requiring corresponding files. Rails v7 uses zeitwerk to automatically load/reload files by relying on the 'file name' to 'constant name' relationship. That's why folders map to namespaces and files map to classes/modules.

To fix your issue put any shared code where it can be grabbed with require. Type $LOAD_PATH in the console:

>> $LOAD_PATH
=> 
["/home/alex/code/stackoverflow/lib",
 "/home/alex/code/stackoverflow/vendor",
 "/home/alex/code/stackoverflow/app/channels",
 "/home/alex/code/stackoverflow/app/controllers",
 "/home/alex/code/stackoverflow/app/controllers/concerns",
 "/home/alex/code/stackoverflow/app/helpers",
 "/home/alex/code/stackoverflow/app/jobs",
 "/home/alex/code/stackoverflow/app/mailers",
 "/home/alex/code/stackoverflow/app/models",
 "/home/alex/code/stackoverflow/app/models/concerns",

 "/home/alex/code/stackoverflow/engines/question/lib",   # <= engine's lib looks good

 "/home/alex/code/stackoverflow/engines/question/app/components",
 "/home/alex/code/stackoverflow/engines/question/app/controllers",
 "/home/alex/code/stackoverflow/engines/question/app/controllers/concerns",
 ...

Put shared files in engines's lib directory. Since we're outside of app directory, rails is not the boss anymore, any path and filename combination will work.

# question/lib/testing_support/blah.rb                   # <= note the filename
module System
  def log_visitor_in
    administrator = create(:visitor)
    visit ccs_cms.login_url
    fill_in 'login_name', with: visitor.login_name
    fill_in 'Password', with: visitor.password
    click_button 'Login'
  end
end

Now that file can be required

# test/test_helper.rb or spec/rails_helper.rb 
# after environment and rails requires

require "testing_support/blah"                           # => loads System module

# ...

That's it, use it in your spec

require 'rails_helper'
RSpec.describe "order_payment_feature", type: :system do
  include System # include is for modules; now we have its functions in this spec

  before { log_visitor_in }

  it 'should accept this answer' do
    visit 'questions/71362333'
    expect(page).to have_content('accepted')
  end
end

Additionally you can require your files any way you wish with an absolute path, regardless of $LOAD_PATH.

require EngineOne::Engine.root + 'spec/support/system/order_functions.rb'

# or something else
Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |f| require f }
like image 121
Alex Avatar answered Sep 04 '25 11:09

Alex