rspec / rspec-rails

RSpec for Rails 6+
https://rspec.info
MIT License
5.14k stars 1.03k forks source link

Wrap tests in Rails executor when configured #2753

Open javierjulio opened 3 months ago

javierjulio commented 3 months ago

This recreates #2712 since the GHA logs are no longer available which we need to address this issue. We should be wrapping the Rails examples with the executor to mimic what Rails v7 does (as noted in #2713) to properly reset state. This has been tested in a test suite for an app but we need to address what issues there is within rspec-rails. This change request originated from #2503 and #2752 which the latter is meant as a temporary fix since this is the expected way to reset state.

javierjulio commented 3 months ago

To properly handle the Rails config switch and still reset state in either case, I figure what really needs to happen here is the following but wasn't sure if this would work (the config conditional) at that level. If it does, then I can update to see where test suite fails.

if ::Rails::VERSION::MAJOR >= 7
  if ::Rails.configuration.active_support.executor_around_test_case
    included do |_other|
      around do |example|
        ::Rails.application.executor.perform { example.call }
      end
    end
  else
    require 'active_support/current_attributes/test_helper'
    include ActiveSupport::CurrentAttributes::TestHelper

    require 'active_support/execution_context/test_helper'
    include ActiveSupport::ExecutionContext::TestHelper
  end
end
javierjulio commented 2 months ago

Thank you for approving CI. I've checked many of the runs and they are failing for the same error.

     NameError:
       uninitialized constant RSpec::Rails::OpenStruct
     # ./spec/rspec/rails/example/view_example_group_spec.rb:190:in `controller'
     # ./spec/rspec/rails/example/view_example_group_spec.rb:196:in `block (3 levels) in <module:Rails>'

I figure this has to do with a new json gem version as I encountered an OpenStruct error with turbo_tests recently. I just don't know how to resolve it here since I don't see any Gemfile.lock files in this repo to confirm. In the meantime, I'll push an update to require ostruct in the reported file.

javierjulio commented 2 months ago

@pirj thank you. This is ready for another test run.

javierjulio commented 2 months ago

@pirj yes, I will reuse the tests from #2752 here. I need help with handling the executor_around_test_case config since there are multiple cases here that I think we need to handle, unless you feel otherwise.

Cases to consider:

This could handle the first and third case, but the second one is tricky because how would we include/use the two test helpers inside the around block?

pirj commented 2 months ago

I see, good point. This leads me to a thought if we can rely that this config will be correctly loaded up by the time we define the RailsExampleGroup. And what is inside those two TestHelpers we include.

JonRowe commented 2 months ago

I've rebased this

ngan commented 2 months ago

We tried this branch on our monolith and tests were failing everywhere. I think the .wrap {...} needs to be the first thing that happens. In Rails, when the option is enabled, the mixin overrides the run method on MiniTest. This means that the wrap happens before all of the before hooks: https://ruby-doc.org/3.2.2/gems/minitest/Minitest/Test.html#method-i-run

ngan commented 2 months ago

This worked for us:

RSpec::Core::Example.prepend(
  Module.new do
    def run(...)
      Rails.application.executor.perform { super }
    end
  end
)

I think we'd have to do the same here since context-level before hooks run before around hooks. Essentially, modify Example#run.