What is RSpec?

RSpec is a domain-specific language testing tool written in Ruby to test Ruby code. It is a behavior-driven development framework, used for the most part in Rails applications, although you can use it outside of Ruby on Rails and without any framework as we’ll see in this post.

How to install RSpec

You can install RSpec in a couple of ways, one way is to install it directly into your gem path by running $ gem install rspec. When you install RSpec you’ll get 5 other supporting gems installed for you. Your output might look something like this:

Fetching rspec-core-3.11.0.gem
Fetching rspec-3.11.0.gem
Fetching rspec-support-3.11.1.gem
Fetching diff-lcs-1.5.0.gem
Fetching rspec-expectations-3.11.1.gem
Fetching rspec-mocks-3.11.1.gem
Successfully installed rspec-support-3.11.1
Successfully installed rspec-core-3.11.0
Successfully installed diff-lcs-1.5.0
Successfully installed rspec-expectations-3.11.1
Successfully installed rspec-mocks-3.11.1
Successfully installed rspec-3.11.0
6 gems installed

Another way to install RSpec is to run $ bundle init in the terminal from your working directory. $ bundle init generates a Gemfile in the current working directory. If you take this route to install RSpec, you can add gems manually to the Gemfile or do $ bundle add rspec which will add RSpec to your Gemfile and install it for you. At this point, you have RSpec installed and you’re ready to take it for a spin.

Test-Driven Development (TDD) with RSpec

In the spirit of TDD, we’ll write a failing test first describing what we want our Ruby program to do, then run the tests, see it fail and then proceed to fix it. To start, you can run this command from your working directory in your terminal:

$ rspec --init

This command gives you an initialization point from which to build your tests. Running this command give us a a directory and a .rspec file. Inside of the directory is another file, spec_helper.rb. Our working directory should look like this:

tree -a
.
├── .rspec
└── spec
└── spec_helper.rb

1 directory, 2 files

If you look inside spec_helper.rb, everything you need to know about these generated files is stated:

# This file was generated by the `rspec --init` command. Conventionally, all

# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.

# The generated `.rspec` file contains `--require spec_helper` which will cause

# this file to always be loaded, without a need to explicitly require it in any

# files.

With that, let’s write our first spec:

RSpec.describe Human, "#greet" do
it "should greet successfully" do
human = Human.new

      expect(human.greet).to eq "Hello, world!"
    end

end

The content of the spec above is in human_spec.rb. The conversion is to prefix _spec.rb with whatever class or object you want to test. In our case we want to test a Human class whose instances can greet.

The first line is a class method describe, that describes the Human class we want to test, and the method in the Human class that we want to describe, #greet.

Our directory now looks something like this:

tree -a
.
├── .rspec
└── spec
├── human_spec.rb
└── spec_helper.rb

1 directory, 3 files

At this point, when we run our tests, things should fail:

rspec

An error occurred while loading ./spec/human_spec.rb.
Failure/Error:
RSpec.describe Human, "#greet" do
it "should greet successfully" do
human = Human.new

        expect(human.greet).to eq "Hello, world!"
      end
    end

NameError:
uninitialized constant Human

# ./spec/human_spec.rb:2:in `<top (required)>'

No examples found.

Finished in 0.00003 seconds (files took 0.30025 seconds to load)
0 examples, 0 failures, 1 error occurred outside of examples

The spec is failing. We’ve got an uninitialized constant Human. We haven’t created that class yet. Let’s create that class and require it.

We require the Human class and so our file looks like this:

require './human'

RSpec.describe Human, "#greet" do
it "should greet successfully" do
human = Human.new

      expect(human.greet).to eq "Hello, world!"
    end

end

Directory structure is now:

tree -a
.
├── .rspec
├── human.rb
└── spec
├── human_spec.rb
└── spec_helper.rb

1 directory, 4 files

After adding a Human class and running the specs again, we have:

rspec
F

Failures:

    1) Human#greet should greet successfully
      Failure/Error: expect(human.greet).to eq "Hello, world!"

      NoMethodError:
        undefined method `greet' for #<Human:0x00007f9414902c70>
      # ./spec/human_spec.rb:7:in `block (2 levels) in <top (required)>'

Finished in 0.00185 seconds (files took 0.10377 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/human_spec.rb:4 # Human#greet should greet successfully

This time we have an undefined method 'greet' for #<Human:0x00007f9414902c70>.

From this point, defining the greet class is all we need to get our specs passing:

class Human
def greet
"Hello, world!"
end
end
rspec
.

Finished in 0.00206 seconds (files took 0.10401 seconds to load)
1 example, 0 failures

This completes our quick introduction to setting put and running RSpec. Running RSpec alone is a lot more involved. I have a post on how to run RSpec tests. In a big codebase, you might want to pick out specific tests to run to save resources.

Last Update: January 07, 2024