Master RSpec with command line
Using RSpec can be a lot more effective and fun if you know well how many options you have at your disposal. Usually, you run the test using the following command:
alternately with the option for running multiple files at once:
rspec spec/your_class_spec.rb spec/directory/ spec/another_class_spec.rb
Above commands are standard, but In many cases, this is enough. But in Ruby, too much awesomeness is not enough awesomeness. This article will cover more advanced usage of the rspec command and less known options but still useful.
If you are not yet familiarized with RSpec, make sure you saw the introduction article, which is a quick introduction to testing Ruby code. Before we begin exploring the command-line RSpec world, I will create a simple test on which we will be testing the different command-line options. Such a real-world example will show you how you can become more effective with testing by using more advanced options.
Given we have the following Person class:
class Person def initialize(age:) @age = age end def adult? @age > 17 end end
And equally simple test in the
require 'spec_helper' RSpec.describe Person do describe '#adult?' do it 'returns false if the given person is less than 18 years old' do person = Person.new(age: 16) expect(person.adult?).to eq(false) end it 'returns true if the given person is more than 17 years old' do person = Person.new(age: 19) expect(person.adult?).to eq(true) end end end
We can run it using the standard rspec command:
The custom output of the tests
When I talk or write about tests, I usually mention that tests are part of the code documentation. Sure, you can just open test files and look through them, but thanks to the
--format option, you can run the test and receive an output that looks like documentation.
By default, the tests’ output consists of dots for passed tests, starts for pending tests, and F characters for those who failed. You can change this behavior by passing the
rspec spec/ --format documentation
Thanks to the above command instead of the default output:
You will receive the following format:
Person #adult? returns false if the given person is less than 18 years old returns true if the given person is more than 17 years old
In the next paragraph, I will show you a cool combination with this option that will allow you to display the "documentation" only for the given method. And just in case you would need to save this piece of documentation for later - you can save the output in the file by using the
rspec spec/ --format documentation --out rspec_documentation.txt
Filtering tests by example name
--example option allows you to run only those tests that are matching the given string. The argument is matched against the full description of the example. The full description is the concatenation of descriptions of the group, including any nested groups and contexts.
If you would like to run only those tests that test if the
#adult? method returns true, you could do the following:
rspec --example ‘#adult? returns true`
With the combination of
--format option, you can even get a list of cases when the method returns true:
rspec --example ‘#adult? returns true` --format documentation
In our case, the output will be the following:
Person #adult? returns true if the given person is more than 17 years old
You don’t even have to open the code editor to answer the question; cool, isn’t it?
Filtering tests by tag name
If you would like to tag some examples that belong to the given feature, you can quickly run them all at once by using the
--tag option. Let’s assume that our spec belongs to the authentication feature:
RSpec.describe Person do describe '#adult?', feature: 'authentication' do it 'returns false if the given person is less than 18 years old' do person = Person.new(age: 16) expect(person.adult?).to eq(false) end it 'returns true if the given person is more than 17 years old' do person = Person.new(age: 19) expect(person.adult?).to eq(true) end end end
When you have many tests, and their execution takes much time, you don’t want to select manually all specs related to the feature you are working on. To run only tests related to authentication, you can use the following command:
rspec spec/ --tag feature:authentication
If would have cases where one spec belongs to more than one feature, than there is no need to worry; arrays of arguments are also supported:
require './person' require 'spec_helper' RSpec.describe Person do describe '#adult?' do it 'returns false if the given person is less than 18 years old', feature: ['authentication', 'other'] do person = Person.new(age: 16) expect(person.adult?).to eq(false) end it 'returns true if the given person is more than 17 years old', feature: 'authentication' do person = Person.new(age: 19) expect(person.adult?).to eq(true) end end end
You can still use the
--tag option as before and filter tests:
rspec spec/ --tag feature:authentication # will run two tests rspec spec/ --tag feature:other # will run one test
Filtering tags with boolean value is also possible, and it’s even easier. If you usually mark slow tests like this:
it 'returns false if the given person is less than 18 years old', slow: true do person = Person.new(age: 16) expect(person.adult?).to eq(false) end
You can run only slow tests by using a special syntax:
rspec spec/ --tag @slow
Which is a shortcut for
You saw a few examples that let you filter the tests by their tags, but how about excluding some tests knowing their tag or tags? It’s as easy as adding
~ before the tag value:
rspec spec/ --tag [email protected]
The above command will run all specs except those marked as slow. Feel free to use other combinations of
--tag with the exclude character.
Running specs… without running the tests
At first, it sounds a little bit weird, but yes, it’s possible to run tests without running them. It’s called a dry run, and you could meet this term before in the software development. The dry run means that you run the process, but it does not affect anything.
If you have a large tests codebase and you would like to see how many specs are there or how many are pending, you can perform a dry run that won’t trigger the tests, but the rspec will behave as we would run them:
rspec spec/ --dry-run
In the large codebase, the output could be the following:
Finished in 0.10594 seconds (files took 20.68 seconds to load) 1429 examples, 0 failures, 5 pending
Nice, it would be great to execute almost 1,5k of tests in less than one second. Another good example of
--dry-run usage is the case where you would like to see the documentation for a given method but without running the tests:
rspec spec/ --format documentation --dry-run --example “#instance_method”
If you are working on fixing failures in the test suite, you will appreciate the
--fail-fast option. If you look for a first failure and don’t want to continue after it appears, use the following command:
rspec spec/ --fail-fast
The RSpec won’t continue running tests after the first failure. If you want to stop after more than one failure, you can specify the number of failed tests after which the tests should be stopped:
rspec spec/ --fail-fast=6
By default, the RSpec won’t stop on the first failure and will continue running tests until all requested tests are executed.
Focusing on failures
If we are talking about the RSpec’s support for failing tests, it is worth mentioning the
--only-failures option. If you are working on fixing failures, you can save the failures into a file and then, with the
--next-failure option, jump to the next failing example after you fix the current one.
To make usage of
--only-failures option, you have to change the RSpec’s config and specify the file where the library will store recent errors:
RSpec.configure do |c| c.example_status_persistence_file_path = "failures.txt" end
Now, when you execute your tests, RSpec will save failing tests in the
example_id | status | run_time | ---------------------------- | ------ | --------------- | ./spec/person_spec.rb[1:1:1] | failed | 0.01228 seconds | ./spec/person_spec.rb[1:1:2] | failed | 0.00015 seconds |
You can now run the first failing test:
rspec spec/ --next-failure
or run all failing tests:
rspec spec/ --only-failures
For those who deal with random failures
One more thing related to the failures. Sometimes when you run the whole test suite, one spec is failing, but when you execute only this one spec, it’s passing - this may be confusing, but it’s not something that the RSpec creators were not aware of.
--bisect option, RSpec will run your tests to find the minimal number of examples needed to reproduce the failure. With such information, it will be easier to find the place in the test where the data is overwritten, and the result of the test is different from the one received when one test is executed.
If you would like to see all available command-line options for RSpec, make sure you visited the official documentation.
The knowledge about command-line options can improve your workflow and make it faster to work with the RSpec tests.