Rubocop 25 line block size and RSpec tests
A recent upgrade of Rubocop brought a new rule into play, that blocks should be no longer than 25 lines. I'm not sure of the rationale for it, because it is not listed in the Ruby style guide.
It used to be that all the cops were based on The Ruby Style Guide, and RuboCop was a way to adhere to the practices set out by the community.
Direction has changed since then, and the scope of RuboCop has expanded to help developers ensure consistency in their code bases in general. This has lead to two things:
- Cops (even those based on The Ruby Style Guide) are now mostly configuratble.
- There are cops for things that aren't mentioned in the Ruby Style Guide, but are still useful for enforcing consistency in a project.
This cop falls in the second category.
Is RSpec using a now discredited approach for code layout, and what reasonable options do I have to reduce block sizes in our RSpec tests?
Short answer is no. DSLs are still cool. :-)
This cop is aimed at large block in the imperative programming sense. As a general guide, it does not apply to DSLs, which are often declarative. For example, having a long routes.rb
file in Rails is perfectly benign. It's just a natural result of a large application, rather than a style violation. (And having a lot of tests is purely awesome.)
Now, RuboCop is pretty smart, but it doesn't know what is a DSL and not, so we can't automatically ignore them. One could argue that we could exclude DSL entry methods of popular frameworks, like Rails routes and RSpec specs. The reasons for not doing so are primarily:
- False negatives. Any class can implement a method, taking a block, with the same name.
- RuboCop is a Ruby analysis tool, and shouldn't really know about external libraries. (Excluding the
/spec
directory is a courtesy until we have a proper extension system, and this can be handled by therubocop-rspec
gem.)
I mean, that's do-able, but turning bunches of spec examples into helper functions in this way seems to be the opposite of the readable approach encouraged by RSpec design.
The bottom line is: RuboCop is there to help us write better code. If our application design is otherwise sound, and we find ourselves making things less readable merely to please RuboCop, then we should filter, configure, or disable the cop. :-)
In response, we have simply set the Rubocop block size rule to a high threshold. But that makes me wonder - what am I missing?
This is a rather blunt tool and, as you're hinting at, you will probably have some false negatives because of it. There are two types of false positives for this cop:
- Files that contain purely declarative DSLs, e.g. Rails routes, RSpec specs.
- Files that have a declarative DSL mixed into mostly imperative code, e.g. an
aasm
state machine declaration in a Rails model.
In the first case, the best solution is to exclude the file or directory, and in the second to use an inline disable.
In your case, you should update your .rubocop.yml
with:
Metrics/BlockLength:
Exclude:
- 'Rakefile'
- '**/*.rake'
- 'test/**/*.rb'
(Note that you need to re-iterate the basic excludes from the default configuration, since the list will be overwritten.)
If a specific block is usually too long, I specify it rather than the files
Metrics/BlockLength:
IgnoredMethods: ['describe', 'context']