Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require rails_helper before configuring RSpec #243

Merged
merged 2 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

### 7.0.1

* fix(RSpec): conditionally adds `--require rails_helper` to cli arguments of `KnapsackPro::Runners::Queue::RSpecRunner`. Version 7.0.0 introduced some fundamental changes, namely fetching, loading and running batches of specs **after** executing suite hooks, so that such hooks are only ran once, not before every batch. As a result, if `rails_helper` is only required in spec files, which is the RSpec default, instead of e.g. in `.rspec`, then some `before(:suite)` hooks, e.g. defined by gems, are registered after suite hooks had already been executed by the test suite. By comparison, RSpec loads all the spec files **before** executing `before(:suite)` hooks.

PR with the above changes: https://github.com/KnapsackPro/knapsack_pro-ruby/pull/243

https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v7.0.0...v7.0.1

### 7.0.0

* __(breaking change)__ RSpec in Queue Mode:
Expand Down
8 changes: 8 additions & 0 deletions lib/knapsack_pro/adapters/rspec_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ def self.has_format_option?(cli_args)
!!parsed_options(cli_args)&.[](:formatters)
end

def self.has_require_rails_helper_option?(cli_args)
(parsed_options(cli_args)&.[](:requires) || []).include?("rails_helper")
end

def self.order_option(cli_args)
parsed_options(cli_args)&.[](:order)
end
Expand All @@ -76,6 +80,10 @@ def self.parse_file_path(id)
id.match(/\A(.*?)(?:\[([\d\s:,]+)\])?\z/).captures.first
end

def self.rails_helper_exists?(test_dir)
File.exist?("#{test_dir}/rails_helper.rb")
end

# private
def self.top_level_group(example)
group = example.metadata[:example_group]
Expand Down
10 changes: 9 additions & 1 deletion lib/knapsack_pro/pure/queue/rspec_pure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ def args_with_seed_option_added_when_viable(order_option, seed, args)
args + ['--seed', seed.value]
end

def prepare_cli_args(args, has_format_option, test_dir)
def prepare_cli_args(args, has_format_option, has_require_rails_helper_option, rails_helper_exists, test_dir)
(args || '').split
.yield_self { args_with_at_least_one_formatter(_1, has_format_option) }
.yield_self { args_with_require_rails_helper_if_needed(_1, has_require_rails_helper_option, rails_helper_exists) }
.yield_self { args_with_default_options(_1, test_dir) }
end

Expand Down Expand Up @@ -75,6 +76,13 @@ def args_with_at_least_one_formatter(cli_args, has_format_option)
cli_args + ['--format', 'progress']
end

def args_with_require_rails_helper_if_needed(cli_args, has_require_rails_helper_option, rails_helper_exists)
return cli_args if has_require_rails_helper_option
return cli_args unless rails_helper_exists

cli_args + ['--require', 'rails_helper']
end

def args_with_default_options(cli_args, test_dir)
new_cli_args = cli_args + [
'--default-path', test_dir,
Expand Down
7 changes: 5 additions & 2 deletions lib/knapsack_pro/runners/queue/rspec_runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ def initialize(adapter_class, rspec_pure, args, stream_error, stream_out)
super(adapter_class)
@adapter_class = adapter_class
@rspec_pure = rspec_pure
has_format_option = @adapter_class.has_format_option?((args || '').split)
@cli_args = rspec_pure.prepare_cli_args(args, has_format_option, test_dir)
args_array = (args || '').split
has_format_option = @adapter_class.has_format_option?(args_array)
has_require_rails_helper_option = @adapter_class.has_require_rails_helper_option?(args_array)
rails_helper_exists = @adapter_class.rails_helper_exists?(test_dir)
@cli_args = rspec_pure.prepare_cli_args(args, has_format_option, has_require_rails_helper_option, rails_helper_exists, test_dir)
@stream_error = stream_error
@stream_out = stream_out
@node_test_file_paths = []
Expand Down
173 changes: 173 additions & 0 deletions spec/integration/runners/queue/rspec_runner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ def generate_specs(spec_helper, rspec_options, spec_batches)
)
end

def create_rails_helper_file(rails_helper)
rails_helper_path = "#{SPEC_DIRECTORY}/rails_helper.rb"
File.open(rails_helper_path, 'w') { |file| file.write(rails_helper) }
end

def stub_spec_batches(batched_tests)
ENV['TEST__SPEC_BATCHES'] = batched_tests.to_json
end
Expand Down Expand Up @@ -327,6 +332,174 @@ def log_command_result(stdout, stderr, status)
end
end

context 'when rails_helper file does not exist' do
it 'does not require the rails_helper file when running RSpec' do
rspec_options = ''

spec_helper = <<~SPEC
require 'knapsack_pro'
KnapsackPro::Adapters::RSpecAdapter.bind
SPEC

spec_a = Spec.new('a_spec.rb', <<~SPEC)
describe 'A_describe' do
it 'A1 test example' do
expect(1).to eq 1
end
end
SPEC

spec_b = Spec.new('b_spec.rb', <<~SPEC)
describe 'B_describe' do
it 'B1 test example' do
expect(1).to eq 1
end
end
SPEC

spec_c = Spec.new('c_spec.rb', <<~SPEC)
describe 'C_describe' do
it 'C1 test example' do
expect(1).to eq 1
end
end
SPEC

generate_specs(spec_helper, rspec_options, [
[spec_a, spec_b],
[spec_c],
])

actual = subject

expect(actual.stdout).to_not include('--require rails_helper')

expect(actual.exit_code).to eq 0
end
end

context 'when rails_helper file exists' do
it 'requires the rails_helper file when running RSpec and runs hooks defined within it' do
rspec_options = ''

spec_helper = <<~SPEC
require 'knapsack_pro'
KnapsackPro::Adapters::RSpecAdapter.bind
SPEC

spec_a = Spec.new('a_spec.rb', <<~SPEC)
describe 'A_describe' do
it 'A1 test example' do
expect(1).to eq 1
end
end
SPEC

spec_b = Spec.new('b_spec.rb', <<~SPEC)
describe 'B_describe' do
it 'B1 test example' do
expect(1).to eq 1
end
end
SPEC

spec_c = Spec.new('c_spec.rb', <<~SPEC)
describe 'C_describe' do
it 'C1 test example' do
expect(1).to eq 1
end
end
SPEC

generate_specs(spec_helper, rspec_options, [
[spec_a, spec_b],
[spec_c],
])

rails_helper = <<~SPEC
RSpec.configure do |config|
config.before(:suite) do
puts 'RSpec_before_suite_hook_from_rails_helper'
end
config.after(:suite) do
puts 'RSpec_after_suite_hook_from_rails_helper'
end
end
SPEC

create_rails_helper_file(rails_helper)

actual = subject

expect(actual.stdout).to include('--require rails_helper')
expect(actual.stdout.scan(/RSpec_before_suite_hook_from_rails_helper/).size).to eq 1
expect(actual.stdout.scan(/RSpec_after_suite_hook_from_rails_helper/).size).to eq 1

expect(actual.exit_code).to eq 0
end

it 'runs suite hooks defined in rails_helper only once, even if file is required multiple times' do
rspec_options = ''

spec_helper = <<~SPEC
require 'knapsack_pro'
KnapsackPro::Adapters::RSpecAdapter.bind
SPEC

spec_a = Spec.new('a_spec.rb', <<~SPEC)
require 'rails_helper'
describe 'A_describe' do
it 'A1 test example' do
expect(1).to eq 1
end
end
SPEC

spec_b = Spec.new('b_spec.rb', <<~SPEC)
require 'rails_helper'
describe 'B_describe' do
it 'B1 test example' do
expect(1).to eq 1
end
end
SPEC

spec_c = Spec.new('c_spec.rb', <<~SPEC)
require 'rails_helper'
describe 'C_describe' do
Pacyfik marked this conversation as resolved.
Show resolved Hide resolved
it 'C1 test example' do
expect(1).to eq 1
end
end
SPEC

generate_specs(spec_helper, rspec_options, [
[spec_a, spec_b],
[spec_c],
])

rails_helper = <<~SPEC
RSpec.configure do |config|
config.before(:suite) do
puts 'RSpec_before_suite_hook_from_rails_helper'
end
config.after(:suite) do
puts 'RSpec_after_suite_hook_from_rails_helper'
end
end
SPEC

create_rails_helper_file(rails_helper)

actual = subject

expect(actual.stdout.scan(/RSpec_before_suite_hook_from_rails_helper/).size).to eq 1
expect(actual.stdout.scan(/RSpec_after_suite_hook_from_rails_helper/).size).to eq 1

expect(actual.exit_code).to eq 0
end
end

context 'when hooks are defined' do
it 'calls RSpec before/after hooks only once for multiple batches of tests' do
rspec_options = ''
Expand Down
54 changes: 54 additions & 0 deletions spec/knapsack_pro/adapters/rspec_adapter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,60 @@
end
end

describe '.has_require_rails_helper_option?' do
subject { described_class.has_require_rails_helper_option?(cli_args) }

context 'when require option is provided as -r' do
ArturT marked this conversation as resolved.
Show resolved Hide resolved
let(:cli_args) { ['-r', 'rails_helper'] }

it { expect(subject).to be true }
end

context 'when require option is provided as --require' do
let(:cli_args) { ['--require', 'rails_helper'] }

it { expect(subject).to be true }
end

context 'when require option is provided without delimiter' do
let(:cli_args) { ['-rrails_helper'] }

it { expect(subject).to be true }
end

context 'when require option is not provided' do
let(:cli_args) { ['--fake', 'value'] }

it { expect(subject).to be false }
end
end

describe '.rails_helper_exists?' do
subject { described_class.rails_helper_exists?(test_dir) }

let(:test_dir) { 'spec_fake' }

context 'when rails_helper exists' do
before do
File.open("#{test_dir}/rails_helper.rb", 'w')
end

after do
FileUtils.rm("#{test_dir}/rails_helper.rb")
end

it { expect(subject).to be true }
end

context 'when rails_helper does not exist' do
before do
FileUtils.rm_f("#{test_dir}/rails_helper.rb")
end

it { expect(subject).to be false }
end
end

describe '.order_option' do
subject { described_class.order_option(cli_args) }

Expand Down
Loading