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

Make RSpec describe_rule DRY #103

Closed
kaka-ruto opened this issue Jan 24, 2020 · 11 comments · Fixed by rubocop/rubocop-rspec#1192
Closed

Make RSpec describe_rule DRY #103

kaka-ruto opened this issue Jan 24, 2020 · 11 comments · Fixed by rubocop/rubocop-rspec#1192
Labels
enhancement New feature or request

Comments

@kaka-ruto
Copy link
Contributor

As is, the describe_rule dsl works with a single string rule

However, sometimes, a couple of rules behave in the same way, and you test them in the same manner.

For example, the following rules are tested in the same manner:

describe_rule :update? do
    succeed 'when user is the workspace owner' do
      let(:context) { { user: user } }
      let(:record) { build_stubbed :workspace, owner: user }
    end
     
    failed 'when user is not the workspace owner' do
      let(:context) { { user: user } }
      let(:record) { build_stubbed :workspace, owner: unauthorized_user }
    end
  end

describe_rule :show? do
    succeed 'when user is the workspace owner' do
      let(:context) { { user: user } }
      let(:record) { build_stubbed :workspace, owner: user }
    end
     
    failed 'when user is not the workspace owner' do
      let(:context) { { user: user } }
      let(:record) { build_stubbed :workspace, owner: unauthorized_user }
    end
  end

This can be refactored to allow more than one rules to be passed to the policy_rule, preferably in an array format. So the above code can be refactored to:

describe_rule [:update?, :show?] do
    succeed 'when user is the workspace owner' do
      let(:context) { { user: user } }
      let(:record) { build_stubbed :workspace, owner: user }
    end
     
    failed 'when user is not the workspace owner' do
      let(:context) { { user: user } }
      let(:record) { build_stubbed :workspace, owner: unauthorized_user }
    end
  end

Is this eligible for a new feature, an extension of the describe_rule dsl?

@kaka-ruto kaka-ruto changed the title Make DSL DRY Make RSpec describe_rule DRY Jan 24, 2020
@palkan palkan added the enhancement New feature or request label Jan 24, 2020
@palkan
Copy link
Owner

palkan commented Jan 24, 2020

Hey @borenho!

Thanks for the proposal.

The idea looks useful in case you have very similar rules (if they 100% similar it's better to use rule aliases though).

On the other hand, auto-generated specs are much harder to debug and use for refactoring, especially when it's a gem specific implementation.

Can we use an Rspec built-in feature instead — shared example? Like this:

shared_examples_for "shared:update?" do
    succeed 'when user is the workspace owner' do
      let(:context) { { user: user } }
      let(:record) { build_stubbed :workspace, owner: user }
    end
     
    failed 'when user is not the workspace owner' do
      let(:context) { { user: user } }
      let(:record) { build_stubbed :workspace, owner: unauthorized_user }
    end
end

describe_rule :update? do
  include_examples "shared:update?"

  # update? specific examples go here
end

describe_rule :show? do
  include_examples "shared:update?"
  
  # show? specific example go here
end

A bit more verbose but we can add rule-specific examples to the shared ones. WDYT?

The only question here: does DSL work in shared examples? I'm not sure. Then making it possible to use DSL in shared examples and contexts would be a useful enhancement.

@dgarwood
Copy link

@palkan I have a similar use case and what you have is exactly what I'm doing.

The DSL works, but rubocop-rspec reports it as a place where 'shared_context' should be used instead of 'shared_examples' because there are no specs defined. I disabled the rule around the shared_example declaration because shared_context did break it.

@palkan
Copy link
Owner

palkan commented May 25, 2020

The DSL works

Nice 👍 Then, we can close the issue, I think.

rubocop-rspec reports it as a place where 'shared_context' should be used instead of 'shared_examples' because there are no specs defined

Yeah, it doesn't understand custom example definition methods (succeed, failed). I will talk to rubocop-rspec maintainers, I think, we can add a list of example definition methods configurable.

@palkan palkan closed this as completed May 25, 2020
@palkan
Copy link
Owner

palkan commented May 25, 2020

It turned out, there is already a similar issue in rubocop-rspec: rubocop/rubocop-rspec#333

pirj added a commit to pirj/action_policy that referenced this issue Nov 8, 2020
Previously, RuboCop RSpec failed to detect RSpec aliases. A number of
cops were failing false offences, and some cops were unable to lint, as
they were skipping locally defined RSpec aliases taking them for
arbitrary blocks and method calls.

See:
 - rubocop/rubocop-rspec#1077
 - rubocop/rubocop-rspec#956
 - palkan#103
pirj added a commit to pirj/action_policy that referenced this issue Nov 8, 2020
Previously, RuboCop RSpec failed to detect RSpec aliases. A number of
cops were failing false offences, and some cops were unable to lint, as
they were skipping locally defined RSpec aliases taking them for
arbitrary blocks and method calls.

See:
 - rubocop/rubocop-rspec#1077
 - rubocop/rubocop-rspec#956
 - palkan#103
@pirj pirj mentioned this issue Nov 8, 2020
2 tasks
pirj added a commit to pirj/action_policy that referenced this issue Nov 17, 2020
Previously, RuboCop RSpec failed to detect RSpec aliases. A number of
cops were failing false offences, and some cops were unable to lint, as
they were skipping locally defined RSpec aliases taking them for
arbitrary blocks and method calls.

See:
 - rubocop/rubocop-rspec#1077
 - rubocop/rubocop-rspec#956
 - palkan#103
palkan pushed a commit that referenced this issue Nov 18, 2020
Previously, RuboCop RSpec failed to detect RSpec aliases. A number of
cops were failing false offences, and some cops were unable to lint, as
they were skipping locally defined RSpec aliases taking them for
arbitrary blocks and method calls.

See:
 - rubocop/rubocop-rspec#1077
 - rubocop/rubocop-rspec#956
 - #103
@dgarwood
Copy link

in case anyone runs into this in the future, this is what I had to do after updating rubocop to 1.21, rubocop-rspec ~> 2.0 and action_policy ~> 0.6.0:

I have some tests that use it_behaves_like in the action policy specs, and the config for Rubocop ended up looking like this:

inherit_gem:
  action_policy: config/rubocop-rspec.yml

...

RSpec:
  Language:
    Includes:
      inherit_mode:
        merge:
          - Examples
      Examples:
      # rules for Action Policy testing DSL
      - it_behaves_like

the inherit_mode is the important part, and can be found here: https://docs.rubocop.org/rubocop/configuration.html#merging-arrays-using-inherit_mode

@palkan not sure if this deserves a mention in the lint docs

@palkan
Copy link
Owner

palkan commented Sep 27, 2021

@dgarwood Thanks for reporting. Looks like this (already fixed) issue: rubocop/rubocop-rspec#1126

I guess, we need to specify inherit_mode in our configuration. @pirj Hey, could you confirm please?

@pirj

This comment has been minimized.

@pirj
Copy link
Contributor

pirj commented Sep 27, 2021

Sorry, I've missed that you are using rubocop 1.21.
It should not require setting inherit_mode in the project config or action_policy default config. If it does, then the bug was not completely fixed, as it was intended to be completely transparent for users.

@pirj
Copy link
Contributor

pirj commented Oct 3, 2021

For the following spec:

RSpec.describe do
  it_behaves_like 'a'
  it_behaves_like 'a'

  describe_rule :show? do
    succeed 'when post is published'
    succeed 'when post is published'
  end
end

and a clean user .rubocop.yml configuration like:

require:
  - rubocop-rspec

inherit_gem:
  action_policy: config/rubocop-rspec.yml

I would expect RSpec/RepeatedIncludeExample to be triggered by both the duplicated success and it_behaves_like.
However, currently, even with the fix, it doesn't merge succeed to Includes/Examples, but rather overwrites it. This results in the duplicate succeed detection, and missing out on the duplicated it_behaves_like.

There are three options:

  1. Tell users to tweak inherit_mode in the user configuration, like you do, @dgarwood . It certainly does the job, and is a feasible workaround. However, burdening end-users of rubocop-rspec with boilerplate was not the original intent.
  2. Submit a fix to action_policy with inherit_mode for what it adds. For this, I'd have to:
  1. Add inherit_mode on rubocop-rspec side

I've checked all three approaches, and they work equally fine, rubocop flags both duplications.

I'll send a PR to rubocop-rspec, adding a note to docs that this feature only works fine with the next minor version of it.

Please keep in mind to update to rubocop-rspec 2.5.1 or 2.6.0 whichever will be released next and include the fix.
You'll be free of necessity to tweak .rubocop.yml in your project.
Before rubocop-rspec is released on RubyGems, you can use it from git:

gem 'rubocop-rspec', github: 'rubocop/rubocop-rspec'

Sorry for the trouble (again).

pirj added a commit to rubocop/rubocop-rspec that referenced this issue Oct 3, 2021
fixes #1126

See palkan/action_policy#103 (comment)

If a third-party gem, e.g. `action_policy`, defines their
`config/default.yml` and supplements to the default RSpec DSL that we
provide in our `config/default.yml`, `rubocop`'s behaviour is to
actually override the configuration, as `inherit_mode` is `override` for
lists by default.

RuboCop has recently [fixed the problem with ignored
`inherit_mode`](rubocop/rubocop#9952) and we've
[bumped to use `rubocop` version that includes the
fix](#1181), but we haven't
adjusted our `config/default.yml` to merge by default.

This is causing both user project RSpec DSL configuration and
third-party gem RSpec DSL configuration to override our default,
rendering our cops blind.

### Example

A new project

```ruby
# Gemfile
source 'https://rubygems.org'

gem 'action_policy'
gem 'rubocop-rspec'
```

```yml
# .rubocop.yml
require:
  - rubocop-rspec

inherit_gem:
  action_policy: config/rubocop-rspec.yml
```

```ruby
# spec/a_spec.rb
RSpec.describe 'A' do
  it_behaves_like 'a'
  it_behaves_like 'a'

  describe_rule :show? do
    succeed 'when post is published'
    succeed 'when post is published'
  end
end
```

Ideally, both the duplicated `it_behaves_like` and `succeed` should be
detected. However, `action_policy`'s `Includes/Examples` setting
overrides ours, and `it_behaves_like` disappears from this list. As a
result, `rubocop` only detects the duplication of `succeed`, but not of
`it_behaves_like`.
pirj added a commit to rubocop/rubocop-rspec that referenced this issue Oct 3, 2021
fixes #1126

See palkan/action_policy#103 (comment)

If a third-party gem, e.g. `action_policy`, defines their
`config/default.yml` and supplements to the default RSpec DSL that we
provide in our `config/default.yml`, `rubocop`'s behaviour is to
actually override the configuration, as `inherit_mode` is `override` for
lists by default.

RuboCop has recently [fixed the problem with ignored
`inherit_mode`](rubocop/rubocop#9952) and we've
[bumped to use `rubocop` version that includes the
fix](#1181), but we haven't
adjusted our `config/default.yml` to merge by default.

This is causing both user project RSpec DSL configuration and
third-party gem RSpec DSL configuration to override our default,
rendering our cops blind.

### Example

A new project

```ruby
# Gemfile
source 'https://rubygems.org'

gem 'action_policy'
gem 'rubocop-rspec'
```

```yml
# .rubocop.yml
require:
  - rubocop-rspec

inherit_gem:
  action_policy: config/rubocop-rspec.yml
```

```ruby
# spec/a_spec.rb
RSpec.describe 'A' do
  it_behaves_like 'a'
  it_behaves_like 'a'

  describe_rule :show? do
    succeed 'when post is published'
    succeed 'when post is published'
  end
end
```

Ideally, both the duplicated `it_behaves_like` and `succeed` should be
detected. However, `action_policy`'s `Includes/Examples` setting
overrides ours, and `it_behaves_like` disappears from this list. As a
result, `rubocop` only detects the duplication of `succeed`, but not of
`it_behaves_like`.
@pirj
Copy link
Contributor

pirj commented Oct 3, 2021

rubocop/rubocop-rspec#1192

pirj added a commit to rubocop/rubocop-rspec that referenced this issue Oct 5, 2021
fixes #1126

See palkan/action_policy#103 (comment)

If a third-party gem, e.g. `action_policy`, defines their
`config/default.yml` and supplements to the default RSpec DSL that we
provide in our `config/default.yml`, `rubocop`'s behaviour is to
actually override the configuration, as `inherit_mode` is `override` for
lists by default.

RuboCop has recently [fixed the problem with ignored
`inherit_mode`](rubocop/rubocop#9952) and we've
[bumped to use `rubocop` version that includes the
fix](#1181), but we haven't
adjusted our `config/default.yml` to merge by default.

This is causing both user project RSpec DSL configuration and
third-party gem RSpec DSL configuration to override our default,
rendering our cops blind.

### Example

A new project

```ruby
# Gemfile
source 'https://rubygems.org'

gem 'action_policy'
gem 'rubocop-rspec'
```

```yml
# .rubocop.yml
require:
  - rubocop-rspec

inherit_gem:
  action_policy: config/rubocop-rspec.yml
```

```ruby
# spec/a_spec.rb
RSpec.describe 'A' do
  it_behaves_like 'a'
  it_behaves_like 'a'

  describe_rule :show? do
    succeed 'when post is published'
    succeed 'when post is published'
  end
end
```

Ideally, both the duplicated `it_behaves_like` and `succeed` should be
detected. However, `action_policy`'s `Includes/Examples` setting
overrides ours, and `it_behaves_like` disappears from this list. As a
result, `rubocop` only detects the duplication of `succeed`, but not of
`it_behaves_like`.
@pirj
Copy link
Contributor

pirj commented Oct 5, 2021

@dgarwood Fixed on rubocop-rspec side. Can you please check if it fixes your issue:

# Gemfile
gem 'rubocop-rspec', github: 'rubocop/rubocop-rspec'

I'm mostly interested that you don't have to include this part any more:

      inherit_mode:
        merge:
          - Examples

pirj added a commit to rubocop/rubocop-capybara that referenced this issue Dec 29, 2022
fixes #1126

See palkan/action_policy#103 (comment)

If a third-party gem, e.g. `action_policy`, defines their
`config/default.yml` and supplements to the default RSpec DSL that we
provide in our `config/default.yml`, `rubocop`'s behaviour is to
actually override the configuration, as `inherit_mode` is `override` for
lists by default.

RuboCop has recently [fixed the problem with ignored
`inherit_mode`](rubocop/rubocop#9952) and we've
[bumped to use `rubocop` version that includes the
fix](rubocop/rubocop-rspec#1181), but we haven't
adjusted our `config/default.yml` to merge by default.

This is causing both user project RSpec DSL configuration and
third-party gem RSpec DSL configuration to override our default,
rendering our cops blind.

### Example

A new project

```ruby
# Gemfile
source 'https://rubygems.org'

gem 'action_policy'
gem 'rubocop-rspec'
```

```yml
# .rubocop.yml
require:
  - rubocop-rspec

inherit_gem:
  action_policy: config/rubocop-rspec.yml
```

```ruby
# spec/a_spec.rb
RSpec.describe 'A' do
  it_behaves_like 'a'
  it_behaves_like 'a'

  describe_rule :show? do
    succeed 'when post is published'
    succeed 'when post is published'
  end
end
```

Ideally, both the duplicated `it_behaves_like` and `succeed` should be
detected. However, `action_policy`'s `Includes/Examples` setting
overrides ours, and `it_behaves_like` disappears from this list. As a
result, `rubocop` only detects the duplication of `succeed`, but not of
`it_behaves_like`.
ydah pushed a commit to rubocop/rubocop-factory_bot that referenced this issue Apr 13, 2023
fixes #1126

See palkan/action_policy#103 (comment)

If a third-party gem, e.g. `action_policy`, defines their
`config/default.yml` and supplements to the default RSpec DSL that we
provide in our `config/default.yml`, `rubocop`'s behaviour is to
actually override the configuration, as `inherit_mode` is `override` for
lists by default.

RuboCop has recently [fixed the problem with ignored
`inherit_mode`](rubocop/rubocop#9952) and we've
[bumped to use `rubocop` version that includes the
fix](rubocop/rubocop-rspec#1181), but we haven't
adjusted our `config/default.yml` to merge by default.

This is causing both user project RSpec DSL configuration and
third-party gem RSpec DSL configuration to override our default,
rendering our cops blind.

### Example

A new project

```ruby
# Gemfile
source 'https://rubygems.org'

gem 'action_policy'
gem 'rubocop-rspec'
```

```yml
# .rubocop.yml
require:
  - rubocop-rspec

inherit_gem:
  action_policy: config/rubocop-rspec.yml
```

```ruby
# spec/a_spec.rb
RSpec.describe 'A' do
  it_behaves_like 'a'
  it_behaves_like 'a'

  describe_rule :show? do
    succeed 'when post is published'
    succeed 'when post is published'
  end
end
```

Ideally, both the duplicated `it_behaves_like` and `succeed` should be
detected. However, `action_policy`'s `Includes/Examples` setting
overrides ours, and `it_behaves_like` disappears from this list. As a
result, `rubocop` only detects the duplication of `succeed`, but not of
`it_behaves_like`.
ydah pushed a commit to rubocop/rubocop-rspec_rails that referenced this issue Mar 27, 2024
fixes #1126

See palkan/action_policy#103 (comment)

If a third-party gem, e.g. `action_policy`, defines their
`config/default.yml` and supplements to the default RSpec DSL that we
provide in our `config/default.yml`, `rubocop`'s behaviour is to
actually override the configuration, as `inherit_mode` is `override` for
lists by default.

RuboCop has recently [fixed the problem with ignored
`inherit_mode`](rubocop/rubocop#9952) and we've
[bumped to use `rubocop` version that includes the
fix](rubocop/rubocop-rspec#1181), but we haven't
adjusted our `config/default.yml` to merge by default.

This is causing both user project RSpec DSL configuration and
third-party gem RSpec DSL configuration to override our default,
rendering our cops blind.

### Example

A new project

```ruby
# Gemfile
source 'https://rubygems.org'

gem 'action_policy'
gem 'rubocop-rspec'
```

```yml
# .rubocop.yml
require:
  - rubocop-rspec

inherit_gem:
  action_policy: config/rubocop-rspec.yml
```

```ruby
# spec/a_spec.rb
RSpec.describe 'A' do
  it_behaves_like 'a'
  it_behaves_like 'a'

  describe_rule :show? do
    succeed 'when post is published'
    succeed 'when post is published'
  end
end
```

Ideally, both the duplicated `it_behaves_like` and `succeed` should be
detected. However, `action_policy`'s `Includes/Examples` setting
overrides ours, and `it_behaves_like` disappears from this list. As a
result, `rubocop` only detects the duplication of `succeed`, but not of
`it_behaves_like`.
ydah pushed a commit to rubocop/rubocop-rspec_rails that referenced this issue Mar 27, 2024
fixes #1126

See palkan/action_policy#103 (comment)

If a third-party gem, e.g. `action_policy`, defines their
`config/default.yml` and supplements to the default RSpec DSL that we
provide in our `config/default.yml`, `rubocop`'s behaviour is to
actually override the configuration, as `inherit_mode` is `override` for
lists by default.

RuboCop has recently [fixed the problem with ignored
`inherit_mode`](rubocop/rubocop#9952) and we've
[bumped to use `rubocop` version that includes the
fix](rubocop/rubocop-rspec#1181), but we haven't
adjusted our `config/default.yml` to merge by default.

This is causing both user project RSpec DSL configuration and
third-party gem RSpec DSL configuration to override our default,
rendering our cops blind.

### Example

A new project

```ruby
# Gemfile
source 'https://rubygems.org'

gem 'action_policy'
gem 'rubocop-rspec'
```

```yml
# .rubocop.yml
require:
  - rubocop-rspec

inherit_gem:
  action_policy: config/rubocop-rspec.yml
```

```ruby
# spec/a_spec.rb
RSpec.describe 'A' do
  it_behaves_like 'a'
  it_behaves_like 'a'

  describe_rule :show? do
    succeed 'when post is published'
    succeed 'when post is published'
  end
end
```

Ideally, both the duplicated `it_behaves_like` and `succeed` should be
detected. However, `action_policy`'s `Includes/Examples` setting
overrides ours, and `it_behaves_like` disappears from this list. As a
result, `rubocop` only detects the duplication of `succeed`, but not of
`it_behaves_like`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants