-
-
Notifications
You must be signed in to change notification settings - Fork 394
Prevent block-only matchers from being used with value expectation target #1125
Prevent block-only matchers from being used with value expectation target #1125
Conversation
def supports_block_expectations? | ||
true | ||
end | ||
|
||
def supports_value_expectations? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible to tell if a matcher supports block expectations, but it's not possible to detect that it doesn't support value expectations, and will be sending call
on a value target.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JonRowe Appreciate if you take a look and give some major direction on how to improve this.
I'm still up to adjust this the way you see fit but need some guidance on specs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry this took so long to get back to you!
# 1) output.to_stderr matcher behaves like an RSpec block-only matcher preserves the symmetric property of `==` | ||
# Failure/Error: raise "Warnings were generated: #{output}" if has_output? | ||
# RuntimeError: | ||
# Warnings were generated: foo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is because warnings are generated by the specs, it should be possible to rewrite the relevant specs warning free...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, valid values were defined as e.g.:
it_behaves_like "an RSpec matcher", :valid_value => lambda { k += 1 },
and that were supposed to check that
expect { k += 1 }.to change { k }
The matcher
here is initialized by change { k }
, and the target is a block.
So the whole point of those two expectations is to make sure that ==
on both the lambda and the matcher return false
when compared with each other.
After this change the target block is defined using a method, and the reason for warnings to appear is that those output
/throw_symbol
/raise_error
targets are actually get called when target is compared, since they are not a block.
Do you think that writing those examples as the following will achieve the same goal?
expect(matcher).not_to eq(-> { valid_block })
expect(-> { valid_block }).not_to eq(matcher)
1da2e36
to
9970f0e
Compare
@JonRowe Builds are green, all specs are in place. |
43520c6
to
0418c67
Compare
Squashed commits. |
@JonRowe Do you think this needs some adjustment? |
I promise this is high on my list for a re-review! |
Hey @pirj great work, looks good. Do you want to merge and then write up a change log? |
Thanks! I'm holding off from merging this, found a problem with
|
05edeb7
to
2e3a7f5
Compare
…rget https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Ruby 1.8-specific workarounds: multiple values for a block parameter (0 for 1) If a block expects an argument, it ought to be provided an argument in 1.8
1779c57
to
771da6d
Compare
matcher.supports_block_expectations? | ||
rescue NoMethodError | ||
false | ||
if matcher.respond_to?(:supports_block_expectations?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is due to have
matcher from rspec-collection_matchers
that is sensitive to methods called on it, i.e. if we call supports_value_expectations?
on have(3).players
, it starts to think that it should match supports_value_expectations?
association, not players
.
The problem is with supports_value_expectations?
, changing supports_block_expectations?
for consistency.
…used-with-value-expectation-target Prevent block-only matchers from being used with value expectation target
Just found this change and it also breaks the following code. Not sure if that was intended: call = lambda do
Reek::CLI::Silencer.silently do
described_class.new ['--foo']
end
end
expect(call).to raise_error(SystemExit) do |error|
expect(error.status).to eq Reek::CLI::Status::DEFAULT_ERROR_EXIT_CODE
end
|
@pirj Yes, both are fine options, although I would prefer the second one rather than chaining multi-line blocks. I was just surprised my existing specs started failing. I fixed my code by changing |
…m-being-used-with-value-expectation-target" This reverts commit f5bf9d8.
This PR has been reverted for now. |
…m-being-used-with-value-expectation-target" This reverts commit f5bf9d8.
I would like to include this again, but we need to address the following situations:
|
…m-being-used-with-value-expectation-target" This reverts commit f5bf9d8.
@JonRowe Sounds good. I can tackle this in the near future. |
Things like |
…m-being-used-with-value-expectation-target" This reverts commit f5bf9d8.
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Also see: #1139 #1125
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Also see: #1139 #1125
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Also see: #1139 #1125
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Also see: #1139 #1125
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Also see: #1139 #1125
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Also see: #1139 #1125
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Also see: #1139 #1125
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Also see: #1139 #1125
…pirj/prevent-block-matchers-from-being-used-with-value-expectation-target Prevent block-only matchers from being used with value expectation target --- This commit was imported from rspec/rspec-expectations@f5bf9d8.
… from pirj/prevent-block-matchers-from-being-used-with-value-expectation-target" This reverts commit f5bf9d8264ffd786a66fe25c3bb1125c93b59d89. --- This commit was imported from rspec/rspec-expectations@9423442.
… from pirj/prevent-block-matchers-from-being-used-with-value-expectation-target" This reverts commit f5bf9d8264ffd786a66fe25c3bb1125c93b59d89. --- This commit was imported from rspec/rspec-expectations@c3b567d.
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html https://rspec.rubystyle.guide/#implicit-block-expectations Spec changes are due to: matcher.matches?(invalid_value) doesn't work with block-only matchers, as no block is passed in, and it fails with a message: 1) RSpec::Matchers::BuiltIn::Change behaves like an RSpec block-only matcher uses the `ObjectFormatter` for `failure_message` Failure/Error: expect(message).to include("detailed inspect") expected "expected `@k` to have changed, but was not given a block" to include "detailed inspect" The redundant (due to existing check in ExpectationTarget) `Proc === @event_proc` checks could not be removed safely as well, since @actual_after is not initialized yet when we haven't executed the block: RuntimeError: Warnings were generated: rspec-dev/repos/rspec-expectations/lib/rspec/matchers/built_in/change.rb:407: warning: instance variable @actual_after not initialized Also see: rspec/rspec-expectations#1139 rspec/rspec-expectations#1125 --- This commit was imported from rspec/rspec-expectations@aaf93ad.
The purpose of this (rather experimental) change is to restrict the usage of:
This syntax was referenced to as:
Problems with this approach
Specs fail quite badly mostly due to the use of this syntax in RSpec Expectations' own specs:
Would you accept this if I change those specs to something more canonical? E.g. like:
The above change didn't work from the first time, but I'll keep trying if that's the right direction.
Related Links
#526
#530
#536
#1066
https://blog.rubystyle.guide/rspec/2019/07/17/rspec-implicit-block-syntax.html
https://www.reddit.com/r/ruby/comments/cejl3q/call_for_discussion_rspec_implicit_block
rubocop/rspec-style-guide#76
rubocop/rubocop-rspec#789
Example fix for matcher libraries that are forced to keep the interface to provide the syntax for their users rodjek/rspec-puppet#767