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.
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 }