Testing Ruby with RSpec


In most cases, developers create tests based on requirements or specifications before coding begins. They are also intended for developers to find errors in their work and for others to understand the code better (i.e., managers and customers).

The advantage of using a Behavior-Driven Development (BDD) test framework like RSpec is that it makes it easier to write correct tests that express how your application should behave using user stories and journeys that convey what is happening within the app.

We'll cover the whole testing process from the initial setup to writing tests using RSpec and then getting them running by looking at different real use cases and scenarios.

This RSpec Ruby tutorial aims to prepare you for writing tests with RSpec in Ruby and gives a general overview of testing with RSpec using Ruby.

So let's dive in for a quick overview.

What is RSpec?

RSpec is a Behavior-Driven Development (BDD) test framework. What does "behavior-driven development" mean? As developers, we generally drive our code with examples of how we expect it to behave.

Behavior-Driven Development (BDD) is a software development process that encourages communication between the business side and the development side of an organization. The acceptance criteria of the tests are usually written in natural language and describe desired specifications of a system. Ruby testing frameworks like RSpec for BDD allow developers to integrate these stories into their development.

Likewise, RSpec allows us to express that behavior by writing examples and using different matchers or assertions in our tests. This will become clear when we begin dealing with tests and examples in this RSpec Ruby tutorial.

RSpec was developed by Dave Astels in 2005, and then some of the original authors created a company called Brakeman which later merged with RSpec. RSpec is currently maintained by an open-source community in conjunction with the core developers at RSpec. When writing this RSpec Ruby tutorial, we have RSpec 3.12.


RSpec and Behavior-Driven Development

Behavior-Driven Development (BDD) is a testing technique that has gained much attention in the Ruby community. To understand BDD, we must first look at how it differs from Test-Driven Development (TDD).

TDD is a testing methodology that involves writing tests before writing code. In fact, you write your test cases first as you're writing code; that way, you can focus more on the functionality of your writing instead of being encumbered by tests. This doesn't mean you stop writing your unit tests after one or two passes. It means that you first write your tests (or specs) and then write the code once you have those in place.

BDD goes a step further and puts more emphasis on behavior. Simply put, BDD doesn't just test functionality; it also tests the intent of your code as it's written. That is to say, in addition to testing that your code works properly, BDD also ensures your code will meet the requirements of your specifications. This flow ensures that even non-technical members get involved in the design and development process since the user stories are written in a language anyone can understand.The TDD vs BDD comparison can be a good source to choose between the two.

What are the ways we can use RSpec to test?

There are several ways we can use RSpec to test. Let's look at some of them in this RSpec Ruby tutorial. You should use whatever suits your team and what it can benefit from the most. We will look at the prerequisites required later in this RSpec Ruby tutorial.

CLI (Command Line Interface)

To use RSpec as a CLI tool, you must install it via RubyGems:

gem install rspec

This can be done on a Windows or Unix-based machine like Linux or Mac OSX.

To run RSpec in the CLI mode, type `rspec` in your terminal. You should get results similar to the one below.

cli mode

RSpec Controller

This is a Rails extension to use RSpec on Rails applications. When writing Rails applications, controllers are the thin layer between your business logic and end users. This code is usually in the form of a class. RSpec is ideal for this because it can easily test small pieces of code – as long as you structure your classes correctly.

An RSpec controller spec would look like this, assuming you had a User controller with a get method that returns all users and loads the index page.

You might notice a lot of Rails syntax in this example, but this gives a general overview of how the controller would look like.

RSpec.describe UsersController do
   describe "GET index" do
     it "assigns @users" do
       team = Team.create
       get :index
       expect(assigns(:teams)).to eq([team])
      it "renders the index template" do
       get :index
       expect(response).to render_template("index")

RSpec Matchers

Matchers are test doubles, meaning they don't contain logic, but just provide data and expectations to the system. Examples of matchers include.

Equality matchers

These are matchers that check that the given object's data matches a provided formatter. They check for equality between properties, methods, strings, numbers, and dates. An example would include keywords such as be, eq, eql, equal. We shall have a look at how these work.

Comparison matchers

These matchers are used to comparing two or more values. They include matchers such as:

  • > - greater than
  • < - less than
  • < = - less than or equal to
  • >= - greater than or equal to
  • be_between - is between

Class type matchers

These are matchers that compare the given object against a provided class or subclass. An example is.

  • be_instance_of - is an instance of a class or subclass
  • be_kind_of - is a kind of a class or subclass

True/False matchers

These matchers are used in the RSpec context where they are expected to fail. It is not necessary to use them in other contexts. They include.

  • be true - is true
  • be false - is false
  • be_truthy - is truthy
  • be_falsey - is falsey
  • be_nil - is nil

Error matchers

These tests are used where we wish to indicate that an error has occurred. They include.

  • raise_error - This can pass the message to the system, such as a log, the error class, or both

What is RSpec used for?

RSpec is used to write automated tests for Ruby code, specifically for testing the behavior of classes, methods, and other code components.

Using RSpec with Ruby, developers can write tests that describe the behavior of their code in a way that is easy to understand and maintain. The RSpec framework provides a rich set of features, such as assertions, matchers, and mocks, which can be used to test different aspects of Ruby code. Overall, RSpec is a powerful tool for ensuring the quality of RSpec Ruby code and helping developers catch bugs and errors early in the development process while performing Ruby automation testing.

  • Functional testing: Such as testing Methods, Classes, Objects, Blocks, etc.
  • Web testing: Using other helper gems such as Selenium, Cucumber, Capybara, and others to test web applications
  • Database testing:Testing database interactions using RSpec and ActiveRecord.
  • Mailing list tests: RSpec can be used to test mailers and the Mail model, particularly testing code that uses other gems.

Those are just a few common use cases, but the things you can test using RSpec are much broader than this.

For the scope of this RSpec Ruby tutorial, we will look at both functional and web testing and examples of each. But first, let’s have a look at the basic RSpec syntax.

Understanding RSpec Syntax

Before we begin writing tests in this RSpec Ruby tutorial, it's important to point out the basic syntax of an RSpec test case. RSpec is easy to use, but there is some basic syntax that we need to know. A simple RSpec module looks more or less like below:

describe BankTests do
   before(:all) do
       # Add code that will run before all of our tests in the describe block.
      context “When testing the Bank classdo
         it "Should initialize the correct bank name " do
           bank = Bank.new("Chase")
   # Add our expect block to do a test on results
            expect(bank.name).to eq "Chase"
   after(:all) do
       # Add code that will run after all of our tests in the describe block.

Let's have a look at the keywords in the syntax used above. describe, context, and it, which are the most common, and also look at other keywords that can be used:


describe refers to a block of code that describes the test being implemented. It helps organize our tests in blocks so that we can see how each test fits into one another.

Furthermore, if we use describe before each test, we can easily see which class or method is being tested and which is used for input parameters.


context defines the context of a test. A context describes what is being tested and the expected values a given object should have.


it allows us to give instructions or instructions to our tests in each of our describe blocks. It tells RSpec which specific block or test we are working on. RSpec has different ways of instructing tests, such as using “within,” “begins with,” or “ends with,” where you put the test name that you want RSpec to do.


expect keyword in RSpec matches the values we expect to get back when we run our tests. We use the expect keyword to enter a “matcher,” which is an expectation on one or more values. RSpec has a lot of matchers that can be used for different situations, but in this RSpec Ruby tutorial, we will talk about just two:

  • eq - This is usually used to test for equality such as expect(result).to eq “Chase” above.
  • be - This is usually used to test for object identity such as expect(result.to eq(expected_value)).


match is mostly used to test for the value of an array(such as using the expected parameter instead of using any other kind of matcher), but this can also be used to test for elements in an array, such as match(expected_array).to contain some element.


contains is usually used to check if an element is present in a list, such as expect(result.to contain('Chase')).to be true. It can also be used to check that two lists have the same values by using contains_each and contains_all.

These are just a few matchers that we can use in RSpec. As you will see below, we have covered the most useful ones in our demo.

Additionally, some hooks help us set up test conditions before and after we run our tests. Hooks help us set up data before a test run or after test runs. The hooks can run at the required point during each test cycle. Let’s look at these in the next section of this RSpec Ruby tutorial.

Before and After Hooks in RSpec

In RSpec, we can execute code before and after each test. Before Hooks are functions attached to the tests, which run before the test function is executed. After Hooks are functions attached to the tests, which run after the test function is executed.

Let's look at some hooks available to achieve the same:

  • before(:each) - Used to run code before each of our tests in a “describe.”
  • before(:all) - Used to run code before all of our tests in the “describe” block.
  • before(:context) - This is a specific context that can be used to run code before any test within the context block. This is usually discouraged unless necessary - to reduce state leakage between the method calls.
  • after(:each) - This is another hook that can run code after each of our tests in the “describe” block.
  • after(:all) - This hook can run code after all our tests in the “describe” block.

To illustrate this, look at this example and check the ordering of the method execution.

RSpec.describe 'Ecommerce Tests' do
 before(:context) do
   puts("Before context")
 before(:all) do
   puts("Before all")
 before(:each) do
   puts("Before each")
 context "Test Strings match" do
   it "should match string" do
     expect("Ruby RSPEC tests").to eq("Ruby RSPEC tests")
     puts("--Test Strings match--")
 context "Test Digits context" do
   it "should match string" do
     expect(2022).to eq(2022)
     puts("--Test Digits context--")
 after(:context) do
   puts("After context")
 after(:all) do
   puts("After all")
 after(:each) do
   puts("After each")

After running this, you will notice that the print statements appear in the order in which they will appear in real tests. To run the tests, run this command on your terminal. rspec spec/test_spec.rb


In the next section of this RSpec Ruby tutorial, we can start configuring our local environment for tests.

Setting up our Ruby Environment and Installing RSpec

In this section of the RSpec Ruby tutorial, we will set up our Ruby environment for testing, install RSpec, and include it in our Gemfile (a package manager used for Ruby-based projects). In general, package managers install software that is not part of your operating system's default installation so that you can have the latest versions of libraries and frameworks and edit them at will.


Before proceeding, you need to ensure you have the following.


Have Ruby up and running on your local machine. You may need to install it using your operating system's package manager or by visiting http://rubyinstaller.org/ and downloading the version you want to install.


Ensure you have Google Chrome, or Firefox browser installed. For this specific RSpec Ruby tutorial, we will use Firefox as it has an integrated debugger that aids in debugging.

IDE of your choice

Make sure you have an IDE installed. For this RSpec Ruby tutorial, we will be using VS studio, as it also has an integrated debugger. However, feel free to use your favorite IDE.

Required Gems

Gems in Ruby provide access to specialized libraries, frameworks, and other code (otherwise known as Ruby Gems) that we can use in our projects. Below is a provided Gemfile with the gems you need to have.

Ensure you have the following Gems in your gemfile.

  • RSpec
  • selenium-webdriver


gem 'rspec', '3.11.0'
gem 'selenium-webdriver', '4.1.0'

Note: To make these packages available, you need to run the bundle install command in the root folder where the Gemfile is located as shown below:

>>bundle install

You would expect to see something similar to the one below. Note that the different versions might vary based on the newer versions available when running this.


How to write your first functional test with RSpec Ruby?

Functional unit tests can test how a class or method is used in isolation from other methods/classes in the project. Since these classes and methods are tested in isolation, no other code must have any interference when we write our tests. One of the ways to test functions in isolation is to mock them.

Mocking is a technique that allows us to specify a function with some pre-built data instead of calling the function in our test and waiting for it to finish. Mocking comes in handy in complex functional tests or unit tests.

Unit tests are the foundation of any successful software project since they give you confidence that your code will work as expected. RSpec comes in handy when writing functional tests as it lets you write your tests using a custom syntax, making it very easy to read and aiding testing.

To write functional tests, we will create two files; one will contain the class under test, while the other will be the actual test.

Ensure you have run the rspec init at the root of the directory to generate the necessary files that will be used by Rspec when running the tests by running the command.

>> rspec –init

If successful, you will observe some folders created for you in the directory you are currently in; refer below to how this will look like.


Now that you have included the required gems in your Ruby file and bundled them, go ahead and create two new files called bank.rb outside the spec file and bank_spec.rb inside the spec folder. Your folder structure should look similar to the following.


Note that the spec_helper.rb is a file created automatically after running rspec –init.

How to perform functional unit testing with RSpec Ruby?

In this section of this RSpec Ruby tutorial, we will write a simple class as below that requires the following tests performed on it.

Test Cases

Test Description Test steps Expected Results
Test that the function can initialize a new bank with the given name. Instantiate a bank with a new name. The bank object name should be equal to the given bank name.
Test that a user can deposit an amount into the bank. Call the deposit method and deposit an amount. The balance should reflect the deposit input by the user.
Test that a user can withdraw an amount from the bank Call the withdrawal method and withdraw a particular amount. The balance should reflect the balance minus the amount withdrawn input by the user.


class Bank
def initialize(bank_name, balance = 0, deposit = 0)
@bankName = bank_name
@balance = balance
@deposit = deposit
puts "#{bank_name} created."

def withdrawal(name, amount)
if amount > 0
@balance -= amount
puts "#{name} withdrew $#{amount} from #{@bankName}.  #{name} has #{@balance}."

def deposit(name, amount)
if amount > 0
@balance += amount
puts "#{name} deposited $#{amount} to #{@bankName}. #{name} has #{@balance}."

Note: The above is the class that will act as our function under test.

Write functional unit tests to test our class above in the spec/bank_spec.rb and type in the following.


require 'rspec'
require "spec_helper"
require_relative '../bank.rb'

RSpec.describe 'Bank Class Tests' do
 before(:context) do
   @bank = Bank.new("Wells Fargo")
   @bankName = @bank.instance_variable_get(:@bankName)
   @balance = @bank.instance_variable_get(:@balance)
   @deposit = @bank.instance_variable_get(:@deposit)

 context "Initializing the bank" do
   it "should create a bank with the given name", :bank_name do
     expect(@bankName).to eq("Wells Fargo")
     expect(@balance).to eq(0)
     expect(@deposit).to eq(0)

   it "should have a balance of 0" do
     balance = @bank.instance_variable_get(:@balance)
     expect(balance).to eq(0)

   it "should have a deposit of 0" do
     deposit = @bank.instance_variable_get(:@deposit)
     expect(deposit).to eq(0)

 context "Depositing and Withdrawing to the bank" do
   it "should have a balance of 0" do
     @bank.deposit("Rose", 100)
     balance = @bank.instance_variable_get(:@balance)
     puts balance
     expect(balance).to eq(100)

   it "should have a balance of 50 after withdrawing 50" do
     puts @bank.instance_variable_get(:@balance)
     @bank.withdrawal("Rose", 50)
     balance = @bank.instance_variable_get(:@balance)
     expect(balance).to eq(50)

To run the tests, run the following command within the folder where the tests are.

>> rspec spec/bank_spec.rb

We can run our tests to see if we have any errors by running the following command from our rspec_test directory: rspec.


At this point, we have successfully written our first batch of functional tests inside of RSpec, and hopefully you will get results similar to the below.

How to run specific tests with RSpec tags?

Assuming you wanted only to run some tests and not all of them all the time. We could use a Tags feature to run specific tests with RSpec tags and specify only the ones relevant to our current project.

Tags specify groups of tests we want to execute and can be specified using -tag or --tag flags.

To test this out, run the following on the terminal.

>> rspec --tag bank_name spec/bank_spec.rbterminal-code-ruby

You might have noticed that we have :bank_name tag on the code base for this example, and that is what the tags are used for in RSpec.

RSpec Code Walkthrough


before(:context) sets up the context for a specific test. Here we use it to ensure our bank is initialized before each test.

We also use the instance_variable_get method to get instance variables within that class object.


In this case, we are telling RSpec that in its current iteration of running tests, we want it to find the first it block (a context) and run the test. You can call your methods however you want, as long as they follow Ruby conventions.

We use the expect method to assert the different expectations of our function.

RSpec provides the let variable to the current context. Although we have not used it in this test, it’s important to note that this is an option to provide a variable within a context. For example, we could instantiate the Bank class using a let as below just inside the context and access it using the bank variable.

let(:bank) { Bank.new("Wells Fargo") }

What happens if the tests fail?

Let's try and intentionally make our test fail to observe what happens. We will change the bank balance and deposit to 100 as below and run the test.

    it "should create a bank with the given name", :bank_name
     expect(@bankName).to eq("Wells Fargo")
     expect(@balance).to eq(100)
     expect(@deposit).to eq(100)

Now, when we run the test, we will get results similar to the one below.


You will note that we get why there was an error, and in our case, it’s because the balance did not match the expected balance for our function, and RSpec is complaining about the mismatch between the expected string and the actual string.

How to perform web automation testing with RSpec Ruby?

Selenium is a framework that allows us to write tests against web applications and automate as many common user tasks as our software can support. It works asynchronously; rather than waiting for the server to respond at a fixed pace, we can set the wait time limits, and our test code will decide when it should wait and continue the test suite.

However, we have covered more details about Selenium in a separate blog.

In this RSpec Ruby tutorial, we will use the eCommerce website to run a few simple tests with RSpec for demonstration purposes.

Create a new file under ecommerce_spec.rb under the spec folder and type in the following:

require 'selenium-webdriver'

RSpec.describe 'Ecommerce Tests' do
  before(:context) do
    # @driver = Selenium::WebDriver.for(:firefox)

    username = "{LT_USERNAME}"
    accessToken = "{LT_ACCESS_KEY}"
    gridUrl = "hub.lambdatest.com/wd/hub"

    capabilities = {
      'LT:Options' => {
        "user" => username,
        "accessKey" => accessToken,
        "build" => "RSpec Ecommerce Test",
        "name" => "RSpec Ecommerce Tests",
        "platformName" => "Windows 11"
      "browserName" => "Firefox",
      "browserVersion" => "100.0",

    @driver = Selenium::WebDriver.for(:remote,
                                      :url => "https://" + username + ":" + accessToken + "@" + gridUrl,
                                      :desired_capabilities => capabilities)

    @wait = Selenium::WebDriver::Wait.new(:timeout => 10)
    @url = "https://ecommerce-playground.lambdatest.io/"

  context "Test that the title matches Your Store" do
    it "should have a home page" do
      expect(@driver.title).to eq("Your Store")

  context "Search for a product" do
    it "should search for a phone" do

      search_box = @driver.find_element(:name, "search")


      search_title = @driver.find_element(:xpath, '//*[@id="entry_212456"]/h1')
      expect(search_title.text).to eq("Search - phone")

  after(:context) do

Code Walkthrough

When the class starts, we require our packages so that they are accessible in our test class. We then define a variable used to override a method of WebDriver (The driver). This is where we create an instance of WebDriver and pass it to the Selenium class.


Next, we define a before(:context) that will run once within the context of this test file, and inside it, we initialize our variables in which we define what URL to visit and what to expect.


Then we initialize our variables to define what URL to visit and what to expect.

This is where we create an instance of Selenium::WebDriver and pass it to our class using the name @driver.

We then use the get method of Selenium::WebDriver to call the baseUrl method that returns a URL, which in this case is https://ecommerce-playground.lambdatest.io.

After the page has loaded fully, we search for the search box element by its name and make our first test using an assertion.

This also applies to the second assertion, where we add the expect for the filtered item element as shown below.


During testing, you may want to get the XPath of an element, and you simply do this by inspecting the element and navigating to the element. From there, you can right-click and copy the XPath from the menu that appears. It is, however, recommended to use ids over XPaths as they are faster for later searches on the page. Use XPaths when it is the best option for that element.


The above is how you would get the XPath of the element we use for our example.

You can also use this free XPath Tester tool that is designed to allow users to test and evaluate XPath expressions or queries against an XML document. It helps ensure that the XPath queries are accurate and return the expected results.

Ending the browser session:

The teardown method is a method that Selenium WebDriver implements to terminate the browser session or run methods that need to be completed at the end of the test run. This is important because the number of browser sessions is limited by the driver's implementation.

A teardown method will be discarded if invoked before the teardown method for all old sessions has been executed. If two or more teardown methods are invoked simultaneously, only one will be executed, and the others will be discarded.

In this method, we invoke @driver.quit to terminate the browser session. It is always important to quit the browser session after the test runs because termination allows closing any additional browser sessions that will otherwise use up unnecessary resources in the computer and lead to performance issues and timeouts as the test would not know when to stop.


After running the script, we should observe the following:


We have our two assertions passing, with 0 errors and 0 failures. But if our tests were to fail, we would get an indication of the failure and what assertions have failed.

How to run Selenium Ruby tests on a cloud grid?

If we want to run Selenium Ruby tests on the cloud Grid, we can change the desired capabilities and browser versions using platforms that provide these capabilities, such as LambdaTest.

LambdaTest is a continuous quality testing platform that allows you to perform Ruby testing at scale for your web applications. Its online browser farm lets you run automated tests across 3000+ real browser environments. LambdaTest also offers easy debugging by providing access to various test logs, including Selenium logs, exception logs, network logs, command logs, and video recordings.

You can also subscribe to the LambdaTest YouTube Channel and stay updated with the latest tutorials around Selenium testing, Playwright, Appium, and more.

Note that you would only replace the before(:context) that has the @driver with the contents below.

After registration and obtaining an access key and username, navigate to the LambdaTest Capabilities Generator and get the capabilities desired and swap them with the local WebDriver capabilities, as shown in the below example.

Make sure you export your Username and Access Key in environment variables so that they can be accessed by the test run.

For Linux/macOS:


For Windows:


require 'selenium-webdriver'

RSpec.describe 'Ecommerce Tests' do
 before(:context) do
   username= "{LT_USERNAME}"
   accessToken= "{LT_ACCESS_TOKEN}"
   gridUrl = "hub.lambdatest.com/wd/hub"

   capabilities = {
     'LT:Options' => {
       "user" => username,
       "accessKey" => accessToken,
       "build" => "RSpec Ecommerce Test",
       "name" => "RSpec Ecommerce Tests",
       "platformName" => "Windows 11"
     "browserName" => "Firefox",
     "browserVersion" => "100.0",

   @driver = Selenium::WebDriver.for(:remote,
            :url => "https://"+username+":"+accessToken+"@"+gridUrl,
            :desired_capabilities => capabilities)

   @wait = Selenium::WebDriver::Wait.new(:timeout => 10)
   @url = "https://ecommerce-playground.lambdatest.io/"


As you can see, the results run on the left tab and will be the most recent run at the top as can be seen below. We can use different browsers and resolutions and watch a video of the test run if we need to.


If we need to get more information about that specific test run, we would click the row with that test run and view more details on the results of our tests along with the steps taken to get to the success/failure of the tests.


If you are a developer or a tester, LambdaTest's Selenium Ruby 101 certification could be a valuable asset for anyone seeking to enhance their Ruby proficiency with Selenium.



RSpec is a powerful and flexible automation testing framework. It is not limited to web development and can be used with other technologies, including CSS and JavaScript. It can also be used with third-party testing and automation testing tools like the popular Selenium.

Automation testing frameworks such as RSpec greatly enhance the web development technologies we use. They help developers to create applications that are testable and maintainable and that are readable, boosting the overall quality of the software application.


Frequently Asked Questions (FAQs)

What is RSpec in Ruby?

RSpec is a popular behavior-driven development (BDD) testing framework for Ruby. It provides a domain-specific language (DSL) for writing tests in a human-readable format, making it easier for developers and stakeholders to understand the behavior of an application.

Is RSpec a gem?

Yes, RSpec is a gem for Ruby. It is a testing framework gem that can be installed using the gem command in the terminal.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Did you find this page helpful?