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

Add deprecation warnings for features to be removed/changed in RSpec 4 #1301

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
### Development
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.12.0...main)

Deprecations:

* Add RSpec 4 deprecation warnings. (Benoit Tigeot, Phil Pirozhkov #1301)

### 3.12.0 / 2022-10-26
[Full Changelog](http://github.com/rspec/rspec-expectations/compare/v3.11.1...v3.12.0)

Expand Down
4 changes: 2 additions & 2 deletions features/syntax_configuration.feature
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Feature: Syntax Configuration
"""
When I run `rspec`
Then the examples should all pass
And the output should contain "Using `should` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax is deprecated"
And the output should contain "Using `should` from rspec-expectations' will be removed in RSpec 4"

Scenario: Disable should syntax
Given a file named "spec/spec_helper.rb" with:
Expand Down Expand Up @@ -88,5 +88,5 @@ Feature: Syntax Configuration
"""
When I run `rspec`
Then the examples should all pass
And the output should not contain "deprecated"
And the output should contain "Using `should` from rspec-expectations' will be removed in RSpec 4"

38 changes: 34 additions & 4 deletions lib/rspec/expectations/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module Expectations
# # or
#
# RSpec::Expectations.configuration
class Configuration
class Configuration # rubocop:disable Metrics/ClassLength
# @private
FALSE_POSITIVE_BEHAVIOURS =
{
Expand All @@ -43,20 +43,42 @@ def initialize
# c.syntax = [:should, :expect]
# end
# end
def syntax=(values)
def syntax=(values) # rubocop:disable Metrics/MethodLength
if self.class.warn_about_syntax?
RSpec.deprecate('Expectations syntax configuration',
:replacement => 'the default `expect` syntax',
:call_site => nil)
end
if Array(values).include?(:expect)
Expectations::Syntax.enable_expect
else
Expectations::Syntax.disable_expect
end

if Array(values).include?(:should)
if self.class.warn_about_syntax?
RSpec.deprecate('`:should` Expectations syntax',
:replacement => 'the default `expect` syntax',
:call_site => nil)
end
Expectations::Syntax.enable_should
else
Expectations::Syntax.disable_should
end
end

# @private
def self.warn_about_syntax?
@warn_about_syntax
end

# @private
def self.warn_about_syntax!
@warn_about_syntax = true
end

@warn_about_syntax = false

# Configures the maximum character length that RSpec will print while
# formatting an object. You can set length to nil to prevent RSpec from
# doing truncation.
Expand Down Expand Up @@ -136,8 +158,16 @@ def backtrace_formatter

# Sets if custom matcher descriptions and failure messages
# should include clauses from methods defined using `chain`.
# @deprecated
# @param value [Boolean]
attr_writer :include_chain_clauses_in_custom_matcher_descriptions
def include_chain_clauses_in_custom_matcher_descriptions=(value)
unless value
RSpec.deprecate("`include_chain_clauses_in_custom_matcher_descriptions` option will be removed in RSpec 4, and will default to true")
end
@include_chain_clauses_in_custom_matcher_descriptions = value
end

attr_reader :include_chain_clauses_in_custom_matcher_descriptions

# Indicates whether or not custom matcher descriptions and failure messages
# should include clauses from methods defined using `chain`. It is
Expand All @@ -149,7 +179,6 @@ def include_chain_clauses_in_custom_matcher_descriptions?
# @private
def reset_syntaxes_to_default
self.syntax = [:should, :expect]
RSpec::Expectations::Syntax.warn_about_should!
end

# @api private
Expand Down Expand Up @@ -226,5 +255,6 @@ def self.configuration

# set default syntax
configuration.reset_syntaxes_to_default
Configuration.warn_about_syntax!
end
end
26 changes: 7 additions & 19 deletions lib/rspec/expectations/syntax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,28 @@ def default_should_host
end

# @api private
# Instructs rspec-expectations to warn on first usage of `should` or `should_not`.
# Enabled by default. This is largely here to facilitate testing.
def warn_about_should!
@warn_about_should = true
end

# @api private
# Generates a deprecation warning for the given method if no warning
# has already been issued.
def warn_about_should_unless_configured(method_name)
return unless @warn_about_should

# Generates a deprecation warning everytime should expectations is used
def warn_about_should
RSpec.deprecate(
"Using `#{method_name}` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax",
:replacement => "the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }`"
"`should =`",
:replacement => "the new `:expect` syntax",
:message => "Using `should` from rspec-expectations' will be removed in RSpec 4"
)

@warn_about_should = false
end

# @api private
# Enables the `should` syntax.
def enable_should(syntax_host=default_should_host)
@warn_about_should = false if syntax_host == default_should_host
return if should_enabled?(syntax_host)

syntax_host.module_exec do
def should(matcher=nil, message=nil, &block)
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(::Kernel.__method__)
::RSpec::Expectations::Syntax.warn_about_should
::RSpec::Expectations::PositiveExpectationHandler.handle_matcher(self, matcher, message, &block)
end

def should_not(matcher=nil, message=nil, &block)
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(::Kernel.__method__)
::RSpec::Expectations::Syntax.warn_about_should
::RSpec::Expectations::NegativeExpectationHandler.handle_matcher(self, matcher, message, &block)
end
end
Expand Down
15 changes: 10 additions & 5 deletions lib/rspec/matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -690,18 +690,23 @@ def all(expected)
# }
# )
#
# @note The `match_regex` alias is deprecated and is not recommended for use.
# It was added in 2.12.1 to facilitate its use from within custom
# matchers (due to how the custom matcher DSL was evaluated in 2.x,
# `match` could not be used there), but is no longer needed in 3.x.
def match(expected)
BuiltIn::Match.new(expected)
end
alias_matcher :match_regex, :match
alias_matcher :an_object_matching, :match
alias_matcher :a_string_matching, :match
alias_matcher :matching, :match

# @note The `match_regex` alias is deprecated and is not recommended for use.
# It was added in 2.12.1 to facilitate its use from within custom
# matchers (due to how the custom matcher DSL was evaluated in 2.x,
# `match` could not be used there), but is no longer needed in 3.x.
# @deprecated
alias_matcher(:match_regex, :match) do |old_desc|
RSpec.deprecate("`match_regex`", :replacement => "`match`")
old_desc.sub(/match/, 'match regex')
end

# An alternate form of `contain_exactly` that accepts
# the expected contents as a single array arg rather
# that splatted out as individual items.
Expand Down
11 changes: 10 additions & 1 deletion lib/rspec/matchers/built_in/has.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ def predicate_matches?(value=true)
if RSpec::Expectations.configuration.strict_predicate_matchers?
value == predicate_result
else
value == !!predicate_result
predicate_actual = predicate_result
if value == !!predicate_actual && value != predicate_actual
RSpec.deprecate(
"`#{predicate_method_name}` returned neither `true` nor `false`, but rather `#{predicate_actual.inspect}`",
:replacement => "`expect(subject.#{predicate_method_name}).to #{value ? "be_truthy" : "be_falsey"}`")
end
value == !!predicate_actual
end
end

Expand Down Expand Up @@ -143,6 +149,9 @@ def predicate
end

def predicate_method_name
unless actual.respond_to?(predicate)
RSpec.deprecate("`#{predicate}` fall back to a present-tense form `#{present_tense_predicate}` will be removed in RSpec 4")
end
actual.respond_to?(predicate) ? predicate : present_tense_predicate
end

Expand Down
5 changes: 4 additions & 1 deletion lib/rspec/matchers/built_in/start_or_end_with.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ def subsets_comparable?
# we still provide this constant until 4.0.
# @deprecated Use StartOrEndWith instead.
# @private
StartAndEndWith = StartOrEndWith
def StartAndEndWith # rubocop:disable Naming/MethodName
RSpec.deprecate("`StartAndEndWith`", :replacement => "`StartOrEndWith`")
StartOrEndWith
end

# @api private
# Provides the implementation for `start_with`.
Expand Down
56 changes: 25 additions & 31 deletions spec/rspec/expectations/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ def delegated?; true; end
config.include_chain_clauses_in_custom_matcher_descriptions = false
expect(config.include_chain_clauses_in_custom_matcher_descriptions?).to be false
end

it "prints a deprecation warning" do
expect(RSpec).to receive(:deprecate).with(
"`include_chain_clauses_in_custom_matcher_descriptions` option will be removed in RSpec 4, and will default to true")
config.include_chain_clauses_in_custom_matcher_descriptions = true
config.include_chain_clauses_in_custom_matcher_descriptions = false
end
end

describe "#max_formatted_output_length=" do
Expand Down Expand Up @@ -157,6 +164,24 @@ def delegated?; true; end
configure_syntax(@orig_syntax)
end

it "warns when should syntax is choosen" do
expect(RSpec).
to receive(:deprecate).
with("Expectations syntax configuration",
:call_site=>nil,
:replacement=>"the default `expect` syntax").
at_least(:once)

expect(RSpec).
to receive(:deprecate).
with("`:should` Expectations syntax",
:call_site=>nil,
:replacement=>"the default `expect` syntax").
at_least(:once)

configure_syntax :should
end

it 'can limit the syntax to :should' do
configure_syntax :should
configured_syntax.should eq([:should])
Expand Down Expand Up @@ -191,37 +216,6 @@ def delegated?; true; end
configure_syntax :expect
end

describe "`:should` being enabled by default deprecation" do
before { configure_default_syntax }

it "warns when the should syntax is called by default" do
expected_arguments = [
/Using.*without explicitly enabling/,
{ :replacement => "the new `:expect` syntax or explicitly enable `:should` with `config.expect_with(:rspec) { |c| c.syntax = :should }`" }
]

expect(RSpec).to receive(:deprecate).with(*expected_arguments)
3.should eq(3)
end

it "includes the call site in the deprecation warning by default" do
expect_deprecation_with_call_site(__FILE__, __LINE__ + 1)
3.should eq(3)
end

it "does not warn when only the should syntax is explicitly configured" do
configure_syntax(:should)
RSpec.should_not receive(:deprecate)
3.should eq(3)
end

it "does not warn when both the should and expect syntaxes are explicitly configured" do
configure_syntax([:should, :expect])
expect(RSpec).not_to receive(:deprecate)
3.should eq(3)
end
end

it 'can re-enable the :should syntax' do
configure_syntax :expect
configure_syntax [:should, :expect]
Expand Down
7 changes: 7 additions & 0 deletions spec/rspec/matchers/aliases_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,13 @@ module RSpec
).with_description('match regex /foo/')
end

it 'prints a deprecation warning for `match_regex`' do
expect(RSpec).
to receive(:deprecate).
with(/match_regex/, :replacement=>"`match`")
match_regex(/foo/).description
end

specify do
expect(
matching(/foo/)
Expand Down
27 changes: 26 additions & 1 deletion spec/rspec/matchers/built_in/be_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@
end

it "passes when actual returns true for :predicates? (present tense)" do
actual = double("actual", :exists? => true, :exist? => true)
actual = double("actual", :exists? => true)
expect(actual).to be_exist
end

it "prints a deprecation warning for present tense fallback" do
expect(RSpec).to receive(:deprecate).with(
"`exist?` fall back to a present-tense form `exists?` will be removed in RSpec 4")
actual = double("actual", :exists? => true)
expect(actual).to be_exist
end

Expand Down Expand Up @@ -85,6 +92,24 @@
expect(actual).to be_happy
}.to fail_with("expected `#{actual.inspect}.happy?` to be truthy, got false")
end

context "when the predicate neither returns true or false" do
it "prints a deprecation warning when actual is truthy" do
expect(RSpec).
to receive(:deprecate).
with("`infinite?` returned neither `true` nor `false`, but rather `-1`",
:replacement => "`expect(subject.infinite?).to be_truthy`")
expect(-Float::INFINITY).to be_infinite
end

it "prints a deprecation warning when actual is truthy" do
expect(RSpec).
to receive(:deprecate).
with("`infinite?` returned neither `true` nor `false`, but rather `nil`",
:replacement => "`expect(subject.infinite?).to be_falsey`")
expect(1).to_not be_infinite
end
end
end

it "fails when actual does not respond to :predicate?" do
Expand Down
20 changes: 20 additions & 0 deletions spec/rspec/matchers/built_in/has_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,26 @@ def o.has_sym?(sym); sym == :foo; end
actual = double("actual", :has_foo? => nil)
expect(actual).not_to have_foo
end

context "when the predicate neither returns true or false" do
it "prints a deprecation warning when actual is falsey" do
expect(RSpec).
to receive(:deprecate).
with("`has_foo?` returned neither `true` nor `false`, but rather `nil`",
:replacement => "`expect(subject.has_foo?).to be_falsey`")
actual = double("actual", :has_foo? => nil)
expect(actual).not_to have_foo
end

it "prints a deprecation warning when actual is truthy" do
expect(RSpec).
to receive(:deprecate).
with('`has_foo?` returned neither `true` nor `false`, but rather `"bar"`',
:replacement => "`expect(subject.has_foo?).to be_truthy`")
actual = double("actual", :has_foo? => "bar")
expect(actual).to have_foo
end
end
end

it "fails if #has_sym?(*args) returns true" do
Expand Down