Focus Your RSpec Workflow
RSpec has options that will help you see your specs more clearly
RSpec is a big library and the way you use it makes a big difference in how efficiently you can run specs. It has a lot of default configuration options that are generated when your application is created, but if your application is older than a few months, it’s likely there are some new and useful configuration options that you might like to add. Here are a couple of RSpec configuration options that make a difference in the way I run my RSpec tests.
The Main Files
It’s worth taking a tour of the RSpec configuration on a new project. When you install RSpec in a Rails application with rails generate rspec:install, you get three configuration files:
- spec_helper.rb, which is supposed to contain generic RSpec configuration.
- rails_helper.rb, which has a require spec_helper statement and is supposed to contain Rails-specific information.
- .rspec, which contains command line arguments which are applied by default.
Usually, your spec file in your rails application will require rails_helper, which also brings in the spec_helper file. Older applications may only have spec_helper, in which case that file has all the configuration.
By default, the .rspec file brings in two options, appended to any command line invocation you make:**_ — color_**, which makes RSpec output in color, and**_ — require spec_helper_**. Technically, that last line means you don’t need to explicitly require **_spec_helper_** in your spec files, even though we all do anyway.
Support Files
The default rails_helper contains the following line, which is commented out by default:
# Dir[Rails.root.join(‘spec/support/**/*.rb’)].each { |f| require f }
If uncommented, this automatically requires each Ruby file in spec/support. I’ve started to uncomment this line and use these files to extend my RSpec configuration, rather than add more configuration to the rails_helper and spec_helper files. Not only is it easier to find different kinds of support, it’s also easier to update configuration when RSpec updates.
Focus and Running a Test Subset
RSpec has a lot of ways to allow you to run only a specified subset of tests. The most commonly used is probably to pass a filename to the RSpec command line, rspec spec/models/user_spec, but there are other ways.
One of the simplest to use is focus. The following configuration option is set by default.
config.filter_run_when_matching :focus
What this means that if RSpec sees any specs that have the focus metadata set, then RSpec will just run the focused tests. If there are no focused tests, then RSpec runs the whole suite.
Making specs focused can be done by just adding :focus to any RSpec block, meaning both it blocks and describe blocks (or any of their synonyms).
There’s an even easier way, which is to just prefix it, describe, or what not with an f. So, fit “it returns a full name” do or fdescribe User do, will both set their respective blocks as focused, and a plain, unadorned rspec command will run just the focused tests.
This is really cool, just remember to undo the focus markers when you are done.
Focus is actually a special case, the more general case uses the — tag command line to match any symbol you want to add. Any symbol in the metadata slot of your RSpec blocks will work. So if you get in the habit of marking JavaScript or other tests as :slow, as in it “purchases the shopping cart”, :slow do, then you can run just the slow tests with:
rspec –tag slow
And run everything but the slow tests by prefixing with ~:
rspec –tag ~slow
Status Persistence and Profiling
My current favorite way of limiting RSpec runs is to limit the run to only tests that are currently failing.
The configuration option that sets this up is a little opaque:
config.example_status_persistence_file_path = “spec/examples.txt”
With this configuration option in place, RSpec generates a file at the end of every one with the passing status of every spec, plus its runtime. The runtime is helpful in tracking slow specs.
With that file in place, the command line invocation rspec — only-failures runs just the specs that are currently marked as failures, while rspec — next-failure runs the current failures and also stops on the first failing spec.
I like rspec — next-failure in part because it allows me to add temporary logging to commonly used methods. Since I know that only one failing spec will run, I know I won’t have to worry about wading through a lot of spurious output.
One thing to mention is that if you are using focus and — only-failures is that focus takes precedence. If you have focused tests set and you try to run using — only-failures, RSpec will ignore the only failures part and will run all the focused specs.
If you haven’t tried these RSpec features recently, give them a shot, they might help you run your tests more efficiently, especially on an older project where the tests are maybe, you know, not the fastest.