diff --git a/BUILD_DETAIL.md b/BUILD_DETAIL.md index 23334ed11a..cd0b6ced6e 100644 --- a/BUILD_DETAIL.md +++ b/BUILD_DETAIL.md @@ -66,6 +66,21 @@ $ bundle exec cucumber $ bin/cucumber ``` +## Snippets + +RSpec Rails uses snippets, self-contained examples that are used to cover +cases and regressions that don't need a full-blown example application to +reproduce. + +Snippets reuse the already installed gems, and don't attempt to install gem +versions that are not on the system already to prevent version mismatches. + +Run with: + +``` +$ script/run_snippets.sh +``` + ## YARD documentation RSpec uses [YARD](https://yardoc.org/) for API documentation on the [rspec.info site](https://rspec.info/). diff --git a/Changelog.md b/Changelog.md index 4946468b89..3fb5618bd5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,9 @@ Enhancements: (Andrew W. Lee, #2372) * Improve request spec "controller" scafold when no action is specified. (Thomas Hareau, #2399) +* Introduce testing snippets concept (Phil Pirozhkov, Benoit Tigeot, #2423) +* Prevent collisions with `let(:name)` for Rails 6.1 and `let(:method_name)` on older + Rails. (Benoit Tigeot, #2461) Bug Fixes: diff --git a/Gemfile b/Gemfile index 5a5c5ce3eb..26b7be09ba 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,4 @@ source "https://rubygems.org" -version_file = File.expand_path('.rails-version', __dir__) -RAILS_VERSION = ENV['RAILS_VERSION'] || (File.exist?(version_file) && File.read(version_file).chomp) || "" gemspec @@ -22,28 +20,11 @@ end gem 'capybara' -MAJOR = - case RAILS_VERSION - when /5-2-stable/ - 5 - when /stable/, nil, false, '' - 6 - else - /(\d+)[\.|-]\d+/.match(RAILS_VERSION).captures.first.to_i - end - -if MAJOR >= 6 - # sqlite3 is an optional, unspecified, dependency and Rails 6.0 only supports `~> 1.4` - gem 'sqlite3', '~> 1.4', platforms: [:ruby] -else - # Similarly, Rails 5.0 only supports '~> 1.3.6'. Rails 5.1-5.2 support '~> 1.3', '>= 1.3.6' - gem 'sqlite3', '~> 1.3.6', platforms: [:ruby] -end - # Until 1.13.2 is released due to Rubygems usage gem 'ffi', '~> 1.12.0' custom_gemfile = File.expand_path('Gemfile-custom', __dir__) eval_gemfile custom_gemfile if File.exist?(custom_gemfile) +eval_gemfile 'Gemfile-sqlite-dependencies' eval_gemfile 'Gemfile-rails-dependencies' diff --git a/Gemfile-sqlite-dependencies b/Gemfile-sqlite-dependencies new file mode 100644 index 0000000000..ace683988a --- /dev/null +++ b/Gemfile-sqlite-dependencies @@ -0,0 +1,20 @@ +version_file = File.expand_path('.rails-version', __dir__) +RAILS_VERSION = ENV['RAILS_VERSION'] || (File.exist?(version_file) && File.read(version_file).chomp) || "" + +MAJOR = + case RAILS_VERSION + when /5-2-stable/ + 5 + when /master/, /stable/, nil, false, '' + 6 + else + /(\d+)[\.|-]\d+/.match(RAILS_VERSION).captures.first.to_i + end + +if MAJOR >= 6 +# sqlite3 is an optional, unspecified, dependency and Rails 6.0 only supports `~> 1.4` + gem 'sqlite3', '~> 1.4', platforms: [:ruby] +else +# Similarly, Rails 5.0 only supports '~> 1.3.6'. Rails 5.1-5.2 support '~> 1.3', '>= 1.3.6' + gem 'sqlite3', '~> 1.3.6', platforms: [:ruby] +end diff --git a/lib/rspec/rails/fixture_support.rb b/lib/rspec/rails/fixture_support.rb index 2161b115c6..12acfdfbd6 100644 --- a/lib/rspec/rails/fixture_support.rb +++ b/lib/rspec/rails/fixture_support.rb @@ -9,6 +9,13 @@ module FixtureSupport include RSpec::Rails::MinitestAssertionAdapter include ActiveRecord::TestFixtures + # @private prevent ActiveSupport::TestFixtures to start a DB transaction. + # Monkey patched to avoid collisions with 'let(:name)' in Rails 6.1 and after + # and let(:method_name) before Rails 6.1. + def run_in_transaction? + use_transactional_tests && !self.class.uses_transaction?(self) + end + included do if RSpec.configuration.use_active_record? include Fixtures @@ -50,13 +57,6 @@ def proxy_method_warning_if_called_in_before_context_scope(method_name) end end end - - if ::Rails.version.to_f >= 6.1 - # @private return the example name for TestFixtures - def name - @example - end - end end end end diff --git a/script/run_build b/script/run_build index e20840e3ca..b7e542bfba 100755 --- a/script/run_build +++ b/script/run_build @@ -20,6 +20,8 @@ fi fold "cukes" run_cukes +fold "snippets" script/run_snippets.sh + if documentation_enforced; then fold "doc check" check_documentation_coverage fi diff --git a/script/run_snippets.sh b/script/run_snippets.sh new file mode 100755 index 0000000000..45146e0277 --- /dev/null +++ b/script/run_snippets.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +( + cd snippets + # This is required to load `bundle/inline` + unset RUBYOPT + for snippet in *.rb; + do + echo Running $snippet + ruby $snippet + done +) diff --git a/snippets/avoid_fixture_name_collision.rb b/snippets/avoid_fixture_name_collision.rb new file mode 100644 index 0000000000..8f1768acc8 --- /dev/null +++ b/snippets/avoid_fixture_name_collision.rb @@ -0,0 +1,57 @@ +if __FILE__ =~ /^snippets/ + fail "Snippets are supposed to be run from their own directory to avoid side " \ + "effects as e.g. the root `Gemfile`, or `spec/spec_helpers.rb` to be " \ + "loaded by the root `.rspec`." +end + +# We opt-out from using RubyGems, but `bundler/inline` requires it +require 'rubygems' + +require "bundler/inline" + +# We pass `false` to `gemfile` to skip the installation of gems, +# because it may install versions that would conflict with versions +# from the main `Gemfile.lock`. +gemfile(false) do + source "https://rubygems.org" + + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + + # Those Gemfiles carefully pick the right versions depending on + # settings in the ENV, `.rails-version` and `maintenance-branch`. + Dir.chdir('..') do + eval_gemfile 'Gemfile-sqlite-dependencies' + # This Gemfile expects `maintenance-branch` file to be present + # in the current directory. + eval_gemfile 'Gemfile-rspec-dependencies' + # This Gemfile expects `.rails-version` file + eval_gemfile 'Gemfile-rails-dependencies' + end + + gem "rspec-rails", path: "../" +end + +# Run specs at exit +require "rspec/autorun" + +require "rails" +require "active_record/railtie" +require "rspec/rails" + +# This connection will do for database-independent bug reports +ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") + +RSpec.configure do |config| + config.use_transactional_fixtures = true +end + +RSpec.describe 'Foo' do + subject { true } + + # Rails 6.1 and after + let(:name) { raise "Should never raise" } + # Before Rails 6.1 + let(:method_name) { raise "Should never raise" } + + it { is_expected.to be_truthy } +end diff --git a/snippets/use_active_record_false.rb b/snippets/use_active_record_false.rb new file mode 100644 index 0000000000..2965223084 --- /dev/null +++ b/snippets/use_active_record_false.rb @@ -0,0 +1,61 @@ +if __FILE__ =~ /^snippets/ + fail "Snippets are supposed to be run from their own directory to avoid side " \ + "effects as e.g. the root `Gemfile`, or `spec/spec_helpers.rb` to be " \ + "loaded by the root `.rspec`." +end + +# We opt-out from using RubyGems, but `bundler/inline` requires it +require 'rubygems' + +require "bundler/inline" + +# We pass `false` to `gemfile` to skip the installation of gems, +# because it may install versions that would conflict with versions +# from the main `Gemfile.lock`. +gemfile(false) do + source "https://rubygems.org" + + git_source(:github) { |repo| "https://github.com/#{repo}.git" } + + # Those Gemfiles carefully pick the right versions depending on + # settings in the ENV, `.rails-version` and `maintenance-branch`. + Dir.chdir('..') do + eval_gemfile 'Gemfile-sqlite-dependencies' + # This Gemfile expects `maintenance-branch` file to be present + # in the current directory. + eval_gemfile 'Gemfile-rspec-dependencies' + # This Gemfile expects `.rails-version` file + eval_gemfile 'Gemfile-rails-dependencies' + end + + gem "rspec-rails", path: "../" +end + +# Run specs at exit +require "rspec/autorun" + +# This snippet describes the case when ActiveRecord is loaded, but +# `use_active_record` is set to `false` in RSpec configuration. + +# Initialization +require "active_record/railtie" +require "rspec/rails" + +# This connection will do for database-independent bug reports +ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") + +# RSpec configuration +RSpec.configure do |config| + config.use_active_record = false +end + +# Rails project code +class Foo +end + +# Rails project specs +RSpec.describe Foo do + it 'does not not break' do + Foo + end +end