Table of Contents
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.