I enjoy using the RSpec Focus filter to run a specific test or subset of tests. However, I often forget to remove this filter, which leaves the tests in a “focused” state. On a good day, I notice the issue and remove the filter. On a bad day, I don’t notice the issue and merge my changes, leaving RSpec in a “focused” state and causing CI to ignore the majority of the test suite.
Before we restore RSpec’s attention, let’s review how to use the RSpec Focus filter.
# run a specific test by adding the "focus" metadata
it "tells RSpec to run this test and this test only", focus: true do
end
# use the alias `fit`, `fdescribe`, or `fcontext`
fit "tells RSpec to run this test and this test only" do
end
To execute focused tests, use either of the following commands:
rspec
rspec --tag focus
Option 1: Use RuboCop to Catch Focused Tests
RuboCop provides an RSpec extension that checks for focused tests. If your team already uses RuboCop, follow the rubocop-rspec installation instructions and RuboCop will notify you when a focused test exists.
Run RuboCop for each Pull Request
Running RuboCop locally is great, but it’s still possible that our future self (or colleagues) will forget to run RuboCop locally and commit a focused test. To prevent focused tests from being merged, we can include RuboCop as part of the Pull Request process.
Using GitHub as an example, let’s create a GitHub Action to run Rubocop and a Branch Protection Rule to prevent the Pull Request from being merged.
- Add a GitHub Action that runs RuboCop:
# github/workflows/rubocop.yml
name: RuboCop
on:
pull_request:
branches:
- main
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Run Rubocop
run: bundle exec rubocop
- Add a GitHub Branch Protection Rule
To add a rule, visit “Settings”, then “Branches”, and then select the option to add a rule. From here, set the “Branch name pattern” to name of the branch you wish to protect. For example, if you wish to protect the “main” branch, set the branch name pattern to “main”.
Next, enable the “Require status checks to pass before merging” option. In the “Search for status checks” field, search for the name of the job (in this example, the name of the job is “lint”). If you run into any issues, consult the GitHub documentation: About branch protection rules.
Once the GitHub Action and Branch Protection Rule are completed, attempting to merge a Pull Request that has not successfully passed RuboCop will look like this:
Use the RuboCop RSpec Extension in Isolation
If your team uses a tool other than RuboCop, you can still use the rubocop-rspec extension without adding all of RuboCop’s opinions. Steps below.
- Follow the rubocop-rspec installation instructions
- At the root of your project, create a new file named
.rubocop.yml
- In the
.rubocop.yml
file, disable default warnings and enable the RSpec Focus warning. Your.rubocop.yml
file will look something like this:
require: rubocop-rspec
# Turn all Rubocop rules off by default
AllCops:
DisabledByDefault: true
# Turn on the RSpec/Focus rule
RSpec/Focus:
Enabled: true
Option 2: Use RSpec to Catch Focused Tests
Another option is to instruct RSpec to raise when a test includes the Focus metadata. (Thank you to Marz Drel for this suggestion.)
# spec/rails_helper.rb
RSpec.configure do |config|
if ENV["CI"]
config.before(:example, :focus) { |example| raise "Focused spec found at
#{example.location}" }
else
config.filter_run_when_matching :focus
end
end
If you prefer the RSpec approach, don’t forget to include “green tests” as part of your Pull Request process. ✅
Hooray! Now, using RuboCop or RSpec, we can alert the team when a Focus filter needs to be removed and ensure all of our tests are hard at work.