rubocop-rspec icon indicating copy to clipboard operation
rubocop-rspec copied to clipboard

false positive on factory

Open luizkowalski opened this issue 6 years ago • 7 comments

I have the following factory:

FactoryBot.define do
  factory :feature_tier, class: 'Services::FeatureTier' do
    feature { create(:feature) }
    tier { create(:tier) }
  end

and I'm getting a false positive (I think) when linting

spec/factories/services_features_tiers.rb:3:5: C: RSpec/EmptyLineAfterExampleGroup: Add an empty line after feature.
    feature { create(:feature) }
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I'm running Rubocop 0.56.0 with rubocop-rspec 1.27.0

luizkowalski avatar Oct 10 '18 11:10 luizkowalski

Thank you for the bug report @luizkowalski.

If you use factories to generate an object with an attribute named identically to certains RSpec DSL methods (describe, context, feature, example_group, etc.) you will get these false positives. Testing your example, I am seeing false positives for RSpec/EmptyExampleGroup, RSpec/EmptyLineAfterExampleGroup and RSpec/MissingExampleGroupArgument.

The real problem is probably that we even run the non-FactoryBot cops against the spec/factories.rb etc. files. I think removing "(?:^|/)spec/" from the AllCops::RSpec::Patterns configuration might be the solution.

bquorning avatar Oct 11 '18 21:10 bquorning

@backus Do you recall why "(?:^|/)spec/" was added as a file pattern in 65c594088597279a674a3e35d1c5cb18b297f181?

bquorning avatar Oct 11 '18 21:10 bquorning

Maybe to catch things like spec/spec_helper.rb and spec/support/ which maybe has example groups in it?

backus avatar Oct 11 '18 21:10 backus

This still causes problems, see:

# migration for the Mymodel

class CreateMymodelFeatures < ActiveRecord::Migration[6.0]
--
def change
  create_table :mymodel_features do |t|
    t.string :foo
    t.string :feature, null: false
# ...

# factory
spec/factories/mymodel_features.rb:6:7: C: RSpec/MissingExampleGroupArgument:
  The first argument to feature should not be empty.
      feature { 'blablabla' }
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

if you call a db column feature and use FactoryBot, it'll cause false positives.

Looks like all factories (as well as app subdirectories) should be excluded from this check.

januszm avatar Jun 05 '20 08:06 januszm

@januszm Pull request is welcome! cc @itay-grudev

I imagine you'd have to tweak this logic https://github.com/rubocop-hq/rubocop-rspec/blob/master/lib/rubocop/cop/rspec/cop.rb#L43 or maybe take a look at the other RuboCop extensions how they manage to do this. Last time I've checked, https://github.com/rubocop-hq/rubocop-minitest didn't have anything related to path checks.

Another option as previously mentioned would be to tweak the pattern here https://github.com/rubocop-hq/rubocop-rspec/blob/master/config/default.yml#L6

pirj avatar Jun 05 '20 10:06 pirj

I'm also experiencing this issue when setting an attribute called context:

spec/factories/tests.rb:6:5: C: RSpec/EmptyExampleGroup: Empty example group detected.
    context { 'MyString' }
    ^^^^^^^
spec/factories/tests.rb:6:5: C: [Corrected] RSpec/EmptyLineAfterExampleGroup: Add an empty line after context.
    context { 'MyString' }
    ^^^^^^^^^^^^^^^^^^^^^^
spec/factories/tests.rb:6:5: C: RSpec/MissingExampleGroupArgument: The first argument to context should not be empty.
    context { 'MyString' }
    ^^^^^^^^^^^^^^^^^^^^^^

Adding the following to .rubocop.yml fixed this:

RSpec/EmptyExampleGroup:
  Exclude:
    - 'spec/factories/**/*'

RSpec/EmptyLineAfterExampleGroup:
  Exclude:
    - 'spec/factories/**/*'

RSpec/MissingExampleGroupArgument:
  Exclude:
    - 'spec/factories/**/*'

But I imagine this should probably be integrated into the gem itself.

boardfish avatar May 13 '21 12:05 boardfish

Common FactoryBot paths can be added to default exclude paths:

Exclude:
  - spec/factories.rb
  - spec/factories/**/*.rb
  - features/support/factories/**/*.rb

However it may break if user config has Exclude section as well since override policy applied AFAIK. But it's done same way for the Include, just mentioned in docs to keep in mind to list default paths, so probably that's an option here as well.

Another way might be to create some sort of mixin for non-top level cops to check if the current block/send is even inside any of example group or shared example/context group. I believe most of the cops shouldn't check for RSpec syntax outside of RSpec blocks. Though it may hit the performance, so maybe trade off solution could be to check that just any of top_level_groups is example group or shared example/context group and just stop investigation if not. It doesn't guarantee absence of false positives in weird RSpec syntax use cases, but it should cover common RSpec usage.

sl4vr avatar Sep 01 '21 09:09 sl4vr