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

RSpec/NoExpectationExample false positives #1385

Closed
boris-petrov opened this issue Sep 12, 2022 · 24 comments · Fixed by #1408
Closed

RSpec/NoExpectationExample false positives #1385

boris-petrov opened this issue Sep 12, 2022 · 24 comments · Fixed by #1408

Comments

@boris-petrov
Copy link

scenario 'test' do
  method
end

private

def method
  expect(1).to eq(1)
end

This gives RSpec/NoExpectationExample but it shouldn't. :)

@ShockwaveNN

This comment was marked as resolved.

@annaswims
Copy link

It also does not accept Capybara::Node::Matchers#assert_text as an assertion in an example like:

    it "is not authorized" do
      assert_text("You are not authorized to perform this action.")
    end

@ShockwaveNN

This comment was marked as outdated.

@Darhazer
Copy link
Member

@annaswims you can add assert_text (and other Capybara assertions) in the language config, so it counts it as an expectation

@annaswims
Copy link

Thanks! I missed the docs

@Darhazer
Copy link
Member

I think we have to account for the expectations in hooks that are in scope. We have the ExpectInHook that would disallow such usage, but it would be frustrating for users to disable that cop just to have NoExpectationExample complain again.

Maybe we also should add the capybara expectations by default to the language (perhaps behind a config option). I'm on a fense for this one, as it would affect things like MultipleExpectations - perhaps the style for capybara scenarios is a bit different.

@rubocop/rubocop-rspec WDYT?

@bquorning
Copy link
Collaborator

bquorning commented Sep 19, 2022

I think this would be much easier to check with dynamic analysis that with static analysis. @pirj Is this something that might be built into RSpec itself one day?

@pirj
Copy link
Member

pirj commented Sep 20, 2022

No, it's not possible (to do this reliably) in RSpec, too, since rspec-mocks/rspec-expectations are not the only way of making expectations, e.g. assert_nil being one I frequently see. There was a discussion about this somewhere in RSpec issue tracker, but I can't quickly find it.

@Darhazer
Copy link
Member

I have reviewed a codebase with the cop applied, and now I think that we may add an AllowedPatterns configuration, so people can just whitelist expect_ and assert_ methods without needing to add them to Language. Though having not them in language also forces people to think how to better express them, e.g. creating custom matchers, instead of merely methods that run expectations

@AlexWayfer
Copy link
Contributor

My case with sequel-enum_values:

I've got RSpec/NoExpectationExample: No expectation found in this example for such code:

shared_examples(
	'for all enum predicate methods'
) do |predicate_method_names, *expectations|
	predicate_method_names.each do |predicate_method_name|
		describe predicate_method_name do
			subject { item_class.new.public_send(predicate_method_name) }

			## rubocop:disable RSpec/NoExpectationExample
			specify do
				Array(expectations).each do |expectation|
					instance_exec(&expectation)
				end
			end
			## rubocop:enable RSpec/NoExpectationExample
		end
	end
end

I understand, meta-programming is difficult for linters like RuboCop.

But I've also got:

Lint/RedundantCopDisableDirective: Unnecessary disabling of RSpec/NoExpectationExample.

That's non-sense!

> bundle exec rubocop spec/sequel/plugins/enum_values_spec.rb

Inspecting 1 file
W

Offenses:

spec/sequel/plugins/enum_values_spec.rb:167:9: W: [Correctable] Lint/RedundantCopDisableDirective: Unnecessary disabling of RSpec/NoExpectationExample.
							## rubocop:disable RSpec/NoExpectationExample
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
spec/sequel/plugins/enum_values_spec.rb:168:8: C: RSpec/NoExpectationExample: No expectation found in this example.
							specify do ...
       ^^^^^^^^^^

1 file inspected, 2 offenses detected, 1 offense autocorrectable

AlexWayfer added a commit to AlexWayfer/sequel-enum_values that referenced this issue Oct 3, 2022
@pirj
Copy link
Member

pirj commented Oct 4, 2022

Unnecessary disabling of RSpec/NoExpectationExample

This would be better reported to the parent project https://github.com/rubocop/rubocop/issues/

meta-programming is difficult for linters like RuboCop.

Not only. Also for people if its sole purpose is to make things dry rather than more readable.

Looking at the spec:

status_enum_values = %w[created selected canceled]

describe 'predicate_methods option' do
  convert_enum_values_to_predicate_methods = lambda do |enum_values|
    enum_values.map { |enum_value| :"#{enum_value}?" }
  end

  status_enum_predicate_methods =
    convert_enum_values_to_predicate_methods.call status_enum_values

  include_examples 'for all enum predicate methods',
    status_enum_predicate_methods,
    -> { expect(subject).to be false }

leads me to a thought that this can be written as

status_enum_values = %w[created selected canceled]

describe 'predicate_methods option' do
  status_enum_values.each do |status_enum_value|
    subject { item_class.new.public_send("#{status_enum_value}?") }

    it 'returns false' do
      expect(subject).to be(false)
    end
  end
end

Or:

status_enum_values = %w[created selected canceled]

shared_examples 'returns false' do |status_enum_value|
  subject { item_class.new.public_send("#{status_enum_value}?") }

  it 'returns false' do
    expect(subject).to be(false)
  end
end

status_enum_values.each do |status_enum_value|
  it_behaves_like 'returns false', status_enum_value
end

Am I missing something? Do those options look worse from your perspective, @AlexWayfer ?

@texpert

This comment was marked as resolved.

@pirj

This comment was marked as resolved.

@texpert

This comment was marked as resolved.

@pirj

This comment was marked as resolved.

@texpert

This comment was marked as resolved.

@pirj

This comment was marked as resolved.

@pirj
Copy link
Member

pirj commented Oct 7, 2022

Personally, I follow the recommendation to use helper methods that is supported by the Effective Testing with RSpec book.

However, since for static checking it creates complications, do you think we can all agree that if something is an expectation, we can name it as such?

def expect_low_value
  expect(product.value).to be_between(0, 50)
end

context 'during summer' do
  it 'has low value' do
    expect_low_value
  end
end

In such a way, we can introduce an option with a default to the cop, say, ConsiderHelperMethodsPrefixes: [expect] that would consider such helper methods as expectations.

Eventually, this can become part of the community RSpec style guide if it gets some traction.

How does that sound?

PS My apologies for an attempt to steal the idea, this is what @Darhazer suggests above.

@texpert

This comment was marked as resolved.

@pirj
Copy link
Member

pirj commented Oct 7, 2022

@pirj
Copy link
Member

pirj commented Oct 7, 2022

expect in after block

RSpec/ExpectInHook disapproves such style, @ShockwaveNN

@pirj
Copy link
Member

pirj commented Oct 7, 2022

Does anyone want to send a PR to introduce AllowedPatterns configuration option, so with default expect_ and assert_ as @Darhazer suggests above?

@ydah
Copy link
Member

ydah commented Oct 9, 2022

If no one plans to send a PR, I will send. How do you like it?

@pirj
Copy link
Member

pirj commented Oct 9, 2022

There's no better candidate than you, @ydah with so many recent contributions, please go for it.

ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 10, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 10, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 10, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 10, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
pirj pushed a commit to ydah/rubocop-rspec that referenced this issue Oct 13, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 14, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 14, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 15, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
pirj pushed a commit to ydah/rubocop-rspec that referenced this issue Oct 15, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
pirj pushed a commit to ydah/rubocop-rspec that referenced this issue Oct 15, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 16, 2022
…mple`

Fix: rubocop#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
pirj pushed a commit to rubocop/rubocop-capybara that referenced this issue Dec 29, 2022
…mple`

Fix: rubocop/rubocop-rspec#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to rubocop/rubocop-factory_bot that referenced this issue Apr 13, 2023
…mple`

Fix: rubocop/rubocop-rspec#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to rubocop/rubocop-rspec_rails that referenced this issue Mar 27, 2024
…mple`

Fix: rubocop/rubocop-rspec#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
ydah added a commit to rubocop/rubocop-rspec_rails that referenced this issue Mar 27, 2024
…mple`

Fix: rubocop/rubocop-rspec#1385

This cop can be customized allowed expectation methods pattern
with `AllowedPatterns`. \Aexpect_ and \Aassert_ are allowed by default.

### @ example `AllowedPatterns: [\Aexpect_]`
```ruby
# bad
it do
  not_expect_something
end

# good
it do
  expect_something
end
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants