Testing, testing, testing: RSpec

Jonathan XIao
6 min readNov 10, 2020
Photo by Allec Gomes on Unsplash

Bugs or to be more concise, the hatred of bugs is a cause that most people can get behind.

In the world of programming, bugs can be a keyboard smashing, headache inducing term that can annoy you for hours on end. You wrote some precious lines of perfect code like the absolute coding prodigy you are; but when you try to run it, everything goes to hell.

You see lines and lines of code that makes no sense at all and you start wondering about the life choices that led you to this moment.

Photo by Markus Spiske on Unsplash

Test-driven development(TDD) and what it has do with RSpec.

Test-driven development (TDD) is a software development process relying on software requirements being converted to test cases before software is fully developed, and tracking all software development by repeatedly testing the software against all test cases. This is opposed to software being developed first and test cases created later. https://en.wikipedia.org/wiki/Test-driven_development

What this means is that when you want to develop something, you plan out ahead of time all the features and requirement that the software has. After you plan it, you convert those into multiple test cases and you test the code that you write against it.

That way when something goes wrong, you know which test cases are failing and which is working, which enables you to pinpoint exactly where the code went wrong.

The test cases that you write also helps give you a blueprint of everything that you need for the the software, which gives you a sense of direction.

RSpec is a ruby gem that helps you write those test cases.

Ok, so how do I use the RSpec gem.

Let’s make a very simple Ruby app to demonstrate the powers of rspec and to see how to use it. This app will take in a full name string and give you back only the first name.

Feel free to code along!

RSpec Setup

First, create a new directory. In this case we will name it Name app. In the terminal:

mkdir name_app

Then we need configure RSpec as a dependency via Bundler. In order to do that let’s create a Gemfile file in our project folder:

name_app % touch Gemfile

Now we need to make sure the RSpec gem is inside of Gemfile:

#Gemfile
source "https://rubygems.org"

gem "rspec"

Open your project directory inside your terminal and type bundle install:

name_app % bundle install               
Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Using bundler 2.1.4
Fetching diff-lcs 1.4.4
Installing diff-lcs 1.4.4
Fetching rspec-support 3.10.0
Installing rspec-support 3.10.0
Fetching rspec-core 3.10.0
Installing rspec-core 3.10.0
Fetching rspec-expectations 3.10.0
Installing rspec-expectations 3.10.0
Fetching rspec-mocks 3.10.0
Installing rspec-mocks 3.10.0
Fetching rspec 3.10.0
Installing rspec 3.10.0
Bundle complete! 1 Gemfile dependency, 7 gems now installed.
Bundled gems are installed into `./.bundle`

Congrats! You have now installed the latest version of RSpec and all related dependencies. You should see an output similar to what is above.

Making your first Spec

It is a common practice for all RSpec tests to be called “specs” and be stored in a directory called spec. Let’s create that:

mkdir spec

Now lets write the first test:

#spec/name_app_spec.rb
describe NameApp do
end

When we use RSpec we always try to describe what the behavior of the software, classes are. If we are describing a class, we need to make sure the class exists.

If ran Rspec now, you would get something like this:

name_app % rspec

NameError:
uninitialized constant NameApp

This makes sense because we never created a NameApp class! So let’s do that. We will first create a lib directory and declare the class in name_app.rb:

#lib/name_app.rb
class NameApp
end

Remember to also require this file in your spec:

#spec/name_app_spec.rb
require 'name_app'
describe NameApp do
end

Now if you run rspec, you will get something similar:

name_app % rspec
No examples found.
Finished in 0.00029 seconds (files took 0.09724 seconds to load)
0 examples, 0 failures

Since we never wrote any examples in the spec test, it’s telling you that there are currently no examples and no failures.

Reminder: make sure you run rspec in your name_app project directory and not in a lib or spec folder by accident!

Writing the first test/example.

The most common way a full name is written is “firstname lastname”. In this way, the first and last name is separated by a space.

In the spirit of TDD we would first describe the function of a method we want to have.

Before we write any code, let’s plan everything out. We want a method that takes in a full name string separated by a space. It will then return to you only the first name of that person. Let’s call this method, .first_name:

There are a lot of new things here so let’s break it down:

— We are using one more describe block to write out the method we are trying to test. According to the plan we wrote above, we are naming the method .first_name.

— The context block tells you in what context a method might be used in. In this case, when we are given a name string separated by a space.

— You fill the it block with what you want the output of the method to be given the context. We want the output to return only the first name when given a full name string.

— The next line, expect(…) is an expression used to determine the expected outputs. The expression (in our case, NameApp.first_name(“john doe”)) is combined with a matcher to determine the expected output of the method. For our example, we are using the eq matcher, but RSpec comes with many, many more.

Now when we run rspec, we should see:

name_app % rspec
F
Failures:1) NameApp.first_name given a name string separated by a space returns first name
Failure/Error: expect(NameApp.first_name("john doe")).to eq("John")

expected: "John"
got: nil

(compared using ==)
# ./spec/name_app_spec.rb:7:in `block (4 levels) in <top (required)>'
Finished in 0.01304 seconds (files took 0.08182 seconds to load)
1 example, 1 failure
Failed examples:rspec ./spec/name_app_spec.rb:6 # NameApp.first_name given a name string separated by a space returns first name

Since we never wrote the .first_name method inside of NameApp class, the rspec test fails!

Now let’s write the minimal code we need to get the test passing:

#lib/name_app.rb
class NameApp
def self.first_name(name)
name.split(" ").first
end
end

Run rspec again:

name_app % rspec
.
Finished in 0.00261 seconds (files took 0.0669 seconds to load)
1 example, 0 failures

Yay! We got the test to pass!!

Photo by S O C I A L . C U T on Unsplash

Sometimes a full name string is given with a “-” between first name and last name: “firstname-lastname”. Let’s write that test out:

We are still describing the .first_name method so we put the second test/condition under describe “.first_name”

Running rspec now:

name_app % rspec
.F
Failures:1) NameApp.first_name given a name string separated by a '-' returns first name
Failure/Error: expect(NameApp.first_name("jonathan-xiao")).to eq("jonathan")

expected: "jonathan"
got: "jonathan-xiao"

(compared using ==)
# ./spec/name_app_spec.rb:14:in `block (4 levels) in <top (required)>'
Finished in 0.02094 seconds (files took 0.1272 seconds to load)
2 examples, 1 failure
Failed examples:rspec ./spec/name_app_spec.rb:13 # NameApp.first_name given a name string separated by a '-' returns first name

Oh no! We can pass this test by adding to our .first_method:

#lib/name_app.rb
class NameApp
def self.first_name(name)
if name.include?("-")
name.split("_").first
else
name.split(" ").first
end
end
end

Running rspec:

name_app % rspec
..
Finished in 0.00254 seconds (files took 0.06869 seconds to load)
2 examples, 0 failures

Well done!! You wrote your first RSpec tests and passed it! Welcome to the wonderful world of testing, but remember that this is only the beginning.

Join me in my next article to learn even more powerful ways to use RSpec!

--

--