From d3e593bcc896cebb646a7764f5c92e5fd255df96 Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Fri, 8 Mar 2024 12:57:03 +0000 Subject: [PATCH 1/5] Only load example group modules when they're used Currently the example group modules for all spec types are always loaded, regardless of which type of spec is being run. Some of those modules force parts of the user's application to load; for example, RSpec::Rails::HelperExampleGroup includes ActionView::TestCase::Behavior and loading ActionView::TestCase loads all of the application's helpers. By autoloading these modules and including them lazily the first time the corresponding spec type is defined, we can avoid loading parts of the user's application unnecessarily. Co-authored-by: Iliana Hadzhiatanasova --- lib/rspec/rails.rb | 19 ++++++++- lib/rspec/rails/configuration.rb | 66 ++++++++++++++++++++++++-------- lib/rspec/rails/example.rb | 16 ++------ 3 files changed, 72 insertions(+), 29 deletions(-) diff --git a/lib/rspec/rails.rb b/lib/rspec/rails.rb index 2aa1f5b412..068ccc8550 100644 --- a/lib/rspec/rails.rb +++ b/lib/rspec/rails.rb @@ -1,6 +1,24 @@ require 'rspec/core' require 'rails/version' +module RSpec + module Rails + autoload :RailsExampleGroup, 'rspec/rails/example/rails_example_group' + autoload :ControllerExampleGroup, 'rspec/rails/example/controller_example_group' + autoload :HelperExampleGroup, 'rspec/rails/example/helper_example_group' + autoload :ModelExampleGroup, 'rspec/rails/example/model_example_group' + autoload :RequestExampleGroup, 'rspec/rails/example/request_example_group' + autoload :RoutingExampleGroup, 'rspec/rails/example/routing_example_group' + autoload :ViewExampleGroup, 'rspec/rails/example/view_example_group' + autoload :FeatureExampleGroup, 'rspec/rails/example/feature_example_group' + autoload :SystemExampleGroup, 'rspec/rails/example/system_example_group' + autoload :MailerExampleGroup, 'rspec/rails/example/mailer_example_group' + autoload :JobExampleGroup, 'rspec/rails/example/job_example_group' + autoload :ChannelExampleGroup, 'rspec/rails/example/channel_example_group' + autoload :MailboxExampleGroup, 'rspec/rails/example/mailbox_example_group' + end +end + # Load any of our adapters and extensions early in the process require 'rspec/rails/adapters' require 'rspec/rails/extensions' @@ -11,7 +29,6 @@ require 'rspec/rails/fixture_support' require 'rspec/rails/file_fixture_support' require 'rspec/rails/fixture_file_upload_support' -require 'rspec/rails/example' require 'rspec/rails/vendor/capybara' require 'rspec/rails/configuration' require 'rspec/rails/active_record' diff --git a/lib/rspec/rails/configuration.rb b/lib/rspec/rails/configuration.rb index 79f64e38d1..facdebb5c4 100644 --- a/lib/rspec/rails/configuration.rb +++ b/lib/rspec/rails/configuration.rb @@ -45,15 +45,40 @@ class Configuration # # @api private def self.add_test_type_configurations(config) - config.include RSpec::Rails::ControllerExampleGroup, type: :controller - config.include RSpec::Rails::HelperExampleGroup, type: :helper - config.include RSpec::Rails::ModelExampleGroup, type: :model - config.include RSpec::Rails::RequestExampleGroup, type: :request - config.include RSpec::Rails::RoutingExampleGroup, type: :routing - config.include RSpec::Rails::ViewExampleGroup, type: :view - config.include RSpec::Rails::FeatureExampleGroup, type: :feature + config.define_derived_metadata(type: :controller) do + config.include RSpec::Rails::ControllerExampleGroup, type: :controller + end + + config.define_derived_metadata(type: :helper) do + config.include RSpec::Rails::HelperExampleGroup, type: :helper + end + + config.define_derived_metadata(type: :model) do + config.include RSpec::Rails::ModelExampleGroup, type: :model + end + + config.define_derived_metadata(type: :request) do + config.include RSpec::Rails::RequestExampleGroup, type: :request + end + + config.define_derived_metadata(type: :routing) do + require "action_controller/test_case" + config.include RSpec::Rails::RoutingExampleGroup, type: :routing + end + + config.define_derived_metadata(type: :view) do + config.include RSpec::Rails::ViewExampleGroup, type: :view + end + + config.define_derived_metadata(type: :feature) do + config.include RSpec::Rails::FeatureExampleGroup, type: :feature + end + config.include RSpec::Rails::Matchers - config.include RSpec::Rails::SystemExampleGroup, type: :system + + config.define_derived_metadata(type: :system) do + config.include RSpec::Rails::SystemExampleGroup, type: :system + end end # @private @@ -192,27 +217,38 @@ def fixture_path=(path) if defined?(::Rails::Controller::Testing) [:controller, :view, :request].each do |type| - config.include ::Rails::Controller::Testing::TestProcess, type: type - config.include ::Rails::Controller::Testing::TemplateAssertions, type: type - config.include ::Rails::Controller::Testing::Integration, type: type + config.define_derived_metadata(type: type) do + config.include ::Rails::Controller::Testing::TestProcess, type: type + config.include ::Rails::Controller::Testing::TemplateAssertions, type: type + config.include ::Rails::Controller::Testing::Integration, type: type + end end end if RSpec::Rails::FeatureCheck.has_action_mailer? - config.include RSpec::Rails::MailerExampleGroup, type: :mailer + config.define_derived_metadata(type: :mailer) do + config.include RSpec::Rails::MailerExampleGroup, type: :mailer + end + config.after { ActionMailer::Base.deliveries.clear } end if RSpec::Rails::FeatureCheck.has_active_job? - config.include RSpec::Rails::JobExampleGroup, type: :job + config.define_derived_metadata(type: :job) do + config.include RSpec::Rails::JobExampleGroup, type: :job + end end if RSpec::Rails::FeatureCheck.has_action_cable_testing? - config.include RSpec::Rails::ChannelExampleGroup, type: :channel + config.define_derived_metadata(type: :channel) do + config.include RSpec::Rails::ChannelExampleGroup, type: :channel + end end if RSpec::Rails::FeatureCheck.has_action_mailbox? - config.include RSpec::Rails::MailboxExampleGroup, type: :mailbox + config.define_derived_metadata(type: :mailbox) do + config.include RSpec::Rails::MailboxExampleGroup, type: :mailbox + end end end diff --git a/lib/rspec/rails/example.rb b/lib/rspec/rails/example.rb index 96f9594b30..c270df3f56 100644 --- a/lib/rspec/rails/example.rb +++ b/lib/rspec/rails/example.rb @@ -1,13 +1,3 @@ -require 'rspec/rails/example/rails_example_group' -require 'rspec/rails/example/controller_example_group' -require 'rspec/rails/example/request_example_group' -require 'rspec/rails/example/helper_example_group' -require 'rspec/rails/example/view_example_group' -require 'rspec/rails/example/mailer_example_group' -require 'rspec/rails/example/routing_example_group' -require 'rspec/rails/example/model_example_group' -require 'rspec/rails/example/job_example_group' -require 'rspec/rails/example/feature_example_group' -require 'rspec/rails/example/system_example_group' -require 'rspec/rails/example/channel_example_group' -require 'rspec/rails/example/mailbox_example_group' +RSpec.warn_deprecation <<~WARNING + Requiring rspec/rails/example.rb no longer has any effect, and it will be removed in RSpec Rails 7. +WARNING From 1036b07508b268e336eea99963135a260338f9aa Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Fri, 8 Mar 2024 23:33:33 +0000 Subject: [PATCH 2/5] Require minitest for backward compatibility Previously ActiveSupport::TestCase was always loaded, so minitest would be required here: https://github.com/rails/rails/blob/v7.1.3.2/activesupport/lib/active_support/test_case.rb#L3 --- lib/rspec/rails/configuration.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/rspec/rails/configuration.rb b/lib/rspec/rails/configuration.rb index facdebb5c4..3e43c660fd 100644 --- a/lib/rspec/rails/configuration.rb +++ b/lib/rspec/rails/configuration.rb @@ -1,3 +1,6 @@ +# minitest was previously always loaded, and users may have come to depend on it. +require "minitest" + # rubocop: disable Metrics/ModuleLength module RSpec module Rails From 800282dd73e7ad7e038535e78b16569251067fe8 Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Sat, 9 Mar 2024 00:09:57 +0000 Subject: [PATCH 3/5] Require files that were previously always loaded --- lib/rspec/rails.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/rspec/rails.rb b/lib/rspec/rails.rb index 068ccc8550..3fe8e28d47 100644 --- a/lib/rspec/rails.rb +++ b/lib/rspec/rails.rb @@ -33,3 +33,6 @@ module Rails require 'rspec/rails/configuration' require 'rspec/rails/active_record' require 'rspec/rails/feature_check' +require 'rspec/rails/view_assigns' +require 'rspec/rails/view_path_builder' +require 'rspec/rails/view_spec_methods' From 012f864f56319d702be5f072d34d7e544b94262a Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Sat, 9 Mar 2024 00:17:46 +0000 Subject: [PATCH 4/5] Remove now redundant requires --- lib/rspec/rails/example/helper_example_group.rb | 2 -- lib/rspec/rails/example/view_example_group.rb | 4 ---- 2 files changed, 6 deletions(-) diff --git a/lib/rspec/rails/example/helper_example_group.rb b/lib/rspec/rails/example/helper_example_group.rb index ea8e0bc40c..ec4af820cb 100644 --- a/lib/rspec/rails/example/helper_example_group.rb +++ b/lib/rspec/rails/example/helper_example_group.rb @@ -1,5 +1,3 @@ -require 'rspec/rails/view_assigns' - module RSpec module Rails # @api public diff --git a/lib/rspec/rails/example/view_example_group.rb b/lib/rspec/rails/example/view_example_group.rb index a03b19958d..e194aa7603 100644 --- a/lib/rspec/rails/example/view_example_group.rb +++ b/lib/rspec/rails/example/view_example_group.rb @@ -1,7 +1,3 @@ -require 'rspec/rails/view_assigns' -require 'rspec/rails/view_spec_methods' -require 'rspec/rails/view_path_builder' - module RSpec module Rails # @api public From be1d197aac923a3dafa4b3fc6c4e65ee91859e46 Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Sat, 9 Mar 2024 00:36:16 +0000 Subject: [PATCH 5/5] Eagerly load example groups before stubbing Rails --- spec/rspec/rails/configuration_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/rspec/rails/configuration_spec.rb b/spec/rspec/rails/configuration_spec.rb index b61b54cf3f..e1e96bbcb5 100644 --- a/spec/rspec/rails/configuration_spec.rb +++ b/spec/rspec/rails/configuration_spec.rb @@ -291,6 +291,7 @@ def in_inferring_type_from_location_environment end it "metadata `type: :request` sets up request example groups" do + require "rspec/rails/example/request_example_group" a_rails_app = double("Rails application") the_rails_module = Module.new { def self.version; end @@ -328,6 +329,7 @@ def self.application; end end it "metadata `type: :feature` sets up feature example groups" do + require "rspec/rails/example/feature_example_group" a_rails_app = double("Rails application") the_rails_module = Module.new { def self.version; end