- Published on
Introduction to RSpec and Testing in Ruby
- Authors
- Name
- Curtis Warcup
RSpec is a testing framework for Ruby. It is the most popular testing framework for Ruby, and is used in conjunction with the Ruby on Rails framework. Let's take a look at how to get started with RSpec.
Basics of RSpec
Install the RSpec gem:
gem install rspec
- It's convention to put all your tests in a
spec
folder.
Writing Tests
Describe
Blocks
- use
describe
to describe a method or classdescribe
tells Rspec what code you're testing- you'll see these messages when you run your tests
- the actual test code goes inside the
describe
block between thedo
andend
# inside spec/example_spec.rb
describe 'some class' do
# some code
end
It
Method
- Similar to other testing frameworks, we can use
it
to describe a test it
is a single test- we can have multiple
it
blocks inside adescribe
block it
accepts aString
as an argument- the string should describe what the test is doing (e.g. "returns true when given a positive number")
require_relative 'boat'
describe Boat do
it 'should create boats' do
expect(Boat.new).to be_a Boat
end
end
Running a test
Run your test with rspec
:
rspec example_spec.rb
You should see something like this in your terminal as a result:
.
Finished in 0.00144 seconds (files took 0.06003 seconds to load)
1 example, 0 failures
Expect
Method
expect
is used to expect a result from a test- it marks an assertion that we expect to be true
describe Boat do
it 'should create boats' do
expect(Boat.new).to be_a Boat
end
it 'should have a name' do
expect(Boat.new.name).to eq 'Boaty McBoatface'
end
end
to
Method
- use the
to
method to complete the assertion (e.g.expect(Boat.new).to be_a Boat
) - after
to
we can use a matcher to check for a specific result
Matchers
- Matchers are used to check for specific results
Common matchers:
eq
- checks for equalitybe_a
- checks for a specific classbe_empty
- checks for an empty arraybe_truthy
- checks for a truthy valuebe_falsey
- checks for a falsy valueinclude
- checks if an array includes a specific value
Test-Driven Development
Let's add some methods on our Boat class and write some tests for them.
We are going to write our tests first, then write the code to make the tests pass.
describe Boat do
it 'should create boats' do
expect(Boat.new).to be_a Boat
end
describe '#allowed_aboard?' do # describe block inside a describe block
end
end
It is valid to nest describe
blocks inside other describe
blocks. This is useful for grouping tests together.
Notice the #
before allowed_aboard?
. This is a convention to indicate that we are testing an instance method.
Let's finish the test for the allowed_aboard?
method:
describe Boat do
it 'should create boats' do
expect(Boat.new).to be_a Boat
end
describe '#allowed_aboard?' do
it 'returns true if inventory includes a life jacket' do
a_boat = Boat.new
allowed = a_boat.allowed_aboard?(['life jacket', 'sun glasses'])
expect(allowed).to be true
end
end
end
- the
allowed_aboard?
method takes an array of inventory as an argument - returns
true
if the inventory includes alife jacket
. - use the
to
matcher to check if the result istrue
If we run this test we will get the following error:
rspec boat_spec.rb
.F
Failures:
1) Boat#allowed_aboard? returns true if inventory includes a life jacket
Failure/Error: allowed = a_boat.allowed_aboard?(['life jacket', 'sun glasses'])
NoMethodError:
undefined method `allowed_aboard?' for #<Boat:0x000000010298e420>
allowed = a_boat.allowed_aboard?(['life jacket', 'sun glasses'])
^^^^^^^^^^^^^^^^
# ./boat_spec.rb:10:in `block (3 levels) in <top (required)>'
Finished in 0.00158 seconds (files took 0.06525 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./boat_spec.rb:8 # Boat#allowed_aboard? returns true if inventory includes a life jacket
This tells us:
- we have an
undefined method
calledallowed_aboard?
for theBoat
class
Let's write the code to make this test pass:
class Boat
def allowed_aboard?(inventory)
inventory.include?('life jacket')
end
end
We should also test out the false
case:
describe Boat do
it 'should create boats' do
expect(Boat.new).to be_a Boat
end
describe '#allowed_aboard?' do
it 'returns true if inventory includes a life jacket' do
a_boat = Boat.new
allowed = a_boat.allowed_aboard?(['life jacket', 'sun glasses'])
expect(allowed).to be true
end
it 'returns false if inventory does not include a life jacket' do
a_boat = Boat.new
allowed = a_boat.allowed_aboard?(['sun glasses'])
expect(allowed).to be false
end
end
end
Setting up a project for RSpec
group :development, :test do
gem 'rspec-rails', '~> 5.1'
gem 'net-smtp', require: false
# ...
end
- Add the
rspec-rails
gem to yourGemfile
- Add the
net-smtp
gem to yourGemfile
- if you have a current
/spec
folder, remove itrm -rf spec