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:
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
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,
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.