At Agira, Technology Simplified, Innovation Delivered, and Empowering Business is what we are passionate about. We always strive to build solutions that boost your productivity.

The Definitive RSpec Tutorial With Examples

  • By Arjunan Subramani
  • March 27, 2020
  • 1247 Views

RSpec is a testing tool for Ruby programmers. RSpec is a Behaviour-Driven Development (BDD) tool. Tests written in RSpec focus on the “behavior” of an application being tested. RSpec does not put emphasis on how the application works but instead on how it behaves, in other words, what the application actually does. RSpec is a DSL for writing tests. In other words, it tries to make tests as readable as possible. Not only that, it also produces output that reads like a story, or spec (“specification”), hence the name.

Setting up RSpec

From the terminal, you can run the following command to install the rspec,

$ gem install rspec

Once the installation is completed now you can change the path to the project directory and run the rspec – -init it will initialize the rspec within the project.

$ mkdir rspec-tutorial
$ cd rspec-tutorial
$ rspec --init

It will create the following file under the project directory,

create .rspec
create spec/spec_helper.rb

That’s it the setup is completed. Now we can run the rspec test it will return the following output,

$ rspec
No examples found.
Finished in 0.00039 seconds (files took 0.12734 seconds to load)
0 examples, 0 failures

Structure of RSpec

RSpec uses the words “describe” and “it” so we can express concepts like a conversation. describe defines a collection of tests . It takes a class or a string as an argument and is passed a block (do/end).

#spec/calculator_spec.rb
describe Calculator do
#..
end

Next it keyword defines an individual example (test). it takes a string argument and passes the block. This block is where our expectations of a method are expressed.

describe Calculator do
it "do something" do
#..
end
end

Testing with RSpec

For this tutorial, I am going to explain the simple application to find adding two numbers by writing a simple calculator function. Now we will create the calculator_spec.rb file under the spec directory and add the following code,

#spec/calculator_spec.rb
describe Calculator do
describe "#add" do
it "returns the sum of two numbers" do
calculator = Calculator.new
expect(calculator.add(2, 3)).to eq(5)
end
end
end

When we run the test we will get the following error,

$ rspec

An error occurred while loading ./spec/calculator_spec.rb.

Failure/Error:
describe Calculator do
describe "#add" do
it "returns the sum of two numbers" do
calculator = Calculator.new
expect(calculator.add(2, 3)).to eq(5)
end
end
end
NameError:
uninitialized constant Calculator
# ./spec/calculator_spec.rb:3:in `<top (required)>'
No examples found.
Finished in 0.00003 seconds (files took 0.24257 seconds to load)
0 examples, 0 failures, 1 error occurred outside of examples

We will get the NameError because we don’t have a Calculator class. From our project root we will create a lib/ folder, and inside, calculator.rb file. And require this file in calculator_spec.rb file.

class Calculator
end

Now we will run the test it will show the NoMethodError like following,

F
Failures:
1) Calculator#add returns the sum of two numbers
Failure/Error: expect(calculator.add(2, 3)).to eq(5)
NoMethodError:
undefined method `add' for #<Calculator:0x0000000002f39a60>
# ./spec/calculator_spec.rb:9:in `block (3 levels) in <top (required)>'
Finished in 0.00453 seconds (files took 0.1237 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/calculator_spec.rb:7 # Calculator#add returns the sum of two numbers

Now we will create add within the calculator.rb file and add the following line

class Calculator
def add(firstNum, secondNum)
end
end

If we were to run rspec this time, we will get our first failure.

F
Failures:
1) Calculator#add returns the sum of two numbers
Failure/Error: expect(calculator.add(2, 3)).to eq(5)
expected: 5
got: nil
(compared using ==)
# ./spec/calculator_spec.rb:9:in `block (3 levels) in <top (required)>'
Finished in 0.14203 seconds (files took 0.12491 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/calculator_spec.rb:7 # Calculator#add returns the sum of two numbers

RSpec clearly provides a reason for the failure: it expected the output to be 7 when we provided the method with (5, 2) as the parameters. Instead, it returned nil. Now we can implement the add method to return the sum of two numbers.

class Calculator
def add(firstNum, secondNum)
firstNum + secondNum
end
end

Then, we will run the test again and it will display a single dot, letting us know that our test has passed.
 

.
Finished in 0.00434 seconds (files took 0.1237 seconds to load)
1 example, 0 failures

Nested Groups

If we are going to test different scenarios of our app, we will group the related test together. We can achieve this using the describe or context methods in Rspec.
For Example,

describe Message do
context "when user is logged in" do
it "displays the messages" do
# ..
end
end
context "when user logged out" do
it "redirects to login page" do
# ..
end
end
end

RSpec Matchers

We’ve discussed how methods such as describe and context are used to set up a structure for our tests, and how it adds an actual test to it. Let’s look at our code again:

expect(calculator.add(2, 3)).to eq(5)

What does expect do, exactly? And what’s the deal with to and eq.
The expect returns an object that responds to the method to. This method to expects to be passed an object that is called matcher. Here eq is matcher. RSpec matchers compare the output of our method with our expected value.

ALSO READ: How To Build A Dating App Like Tinder

RSpec also provide the option to test the Errors as well,

expect{ calculator.multiply(2, 3) }.to raise_error(NoMethodError)

RSpec also allows you to use matchers that depend on the methods defined on the object passed. We also call these are magic matchers. For Example,

expect(nil).to be_nil

The matcher be_nil expects the method nil? to be defined on the object under test. The method nil? is defined on every object in Ruby. And in our case, nil.nil? returns true, of course, so the test would pass.
This test, however, would not pass:

expect(true).to be_nil

Because of true.nil? returns false.
For more details, you can read here for all available matchers.

RSpec Formatters

The Rspec provides the following different type of format for running the test.

  • progress
  • documentation
  • JSON
  • HTML

We can run the RSpec with the specific format by using the – -format or -f with a specific option.

Progress

The default RSpec output is in the “progress” format. In this format, we can see a single dot (.) for the passed test, F for a failed test, E for an error and * for a pending test.

Documentation

We will run RSpec’s documentation format by passing the command line option – -format documentation. With all tests passing the output will look like the document.

$ rspec spec/calculator_spec.rb --format documentation
Calculator
#add
returns the sum of two numbers
#multiply
returns the multiplication of two numbers
Finished in 0.00442 seconds (files took 0.13714 seconds to load)
2 examples, 0 failures

json option will print the output as json format and html will print the output as html format.

ALSO READ: How To Develop A Healthcare Mobile Application For Hospitals

If we want to store the output as file, we need to pass the – -out option with filename, it will store the output in the file.

$ rspec spec/calculator_spec.rb --format html --out calculator.html

RSpec also supports running the test with multiple formats at the same time. Let’s consider we want to store the output as html format and also we want to see the progress of the tests. We will achieve this like following

$ rspec spec/calculator_spec.rb --format html --out calculator.html --format progress

It will store the html output to calculator.html file and print the following output in terminal

..
Finished in 0.00459 seconds (files took 0.13434 seconds to load)
2 examples, 0 failures

Advanced Use Case

Hooks

RSpec provides the execution hooks using these we can run some code before, after or around the test.

describe Order do
before(:all) { Order.prepare_database }
after (:all) { Order.cleanup_database }
end

Let

If we want to reuse the same objects for many test cases we can define these objects using let. Using let the variable lazy loads only when it is used the first time in the test and get cached until that specific test is finished.
For example,

require "calculator"
describe Calculator do
let(:calculator) { Calculator.new }
describe "#add" do
it "returns the sum of two numbers" do
expect(calculator.add(2, 3)).to eq(5)
end
end
describe "#multiply" do
it "returns the multiplication of two numbers" do
expect{ calculator.multiply(2,3) }.to raise_error(NoMethodError)
end
end
end

From the above code, we have reused the calculator object for all out tests.
Use let! if we want to define the variable when the block is defined. This can be useful to populate your database to test queries or scopes. Let! is a nonlazy method, the object will be created before running any test

let!(:article) { Article.create(title: 'RSpec Tutorial') }

Subject

If we have several tests related to the same subject use subject{} to DRY them up. The subject is another version of let.
We can declare the subject like following,

subject(:calculator) { Calculator.new }

The subject will behave the same as let, but it enables the use of one line expectations like following,

it { should be_empty }

Command-line options

The rspec command comes with several options you can use to customize RSpec’s
behavior, including output formats, filtering examples, etc.

ALSO READ: MongoDB In Golang With Examples – A Beginner’s Guide

For a full list of options, run the rspec command with the – -help flag:
$ rspec – -help
– –init option generates the conventional files for an RSpec project.
– – format option to tell RSpec how to format the output.
– –tag option to filter the tests by tags.
– –order option to tell RSpec how to order the files, groups, and tests.
– –out Write output to a file instead of $stdout. This option applies to the previously specified – –format, or the default format if no format is specified.
– –profile option tells us how long each test is taken to run.
Do you find it interesting? you might also like these articles. Top 10 Best Tech Companies For Employees To Work In The USA In 2020 and Top 10 IT Staffing and Recruiting Agencies in the USA.
If you have a business idea in your mind and in search of a reliable web development company, you are in the right place. Hire the best Ruby on Rails developers in the industry from Agira technologies.

Looking for a Tech partner to dominate the digital world?

Arjunan Subramani

Senior Software Engineer | Around 4 Years of experience in Web development and Testing arena. Plus, hands on expertise in Ruby, Ruby on Rails, Watir, Ruby Cucumber.