This post won't go in depth on writing RSpec tests, but it will show you how to configure a .rspec
file and how to use RSpec syntax with an example for a single Ruby class at the bottom.
Configuration
- Ruby 2.0 or higher recommended
- You must use RSpec version 3 or higher. Version 2 & 3 of RSpec are not interchangeable.
- Install RSpec with
gem install rspec
To write a simple RSpec test for a Ruby class, you can start by cd
into your project directory and in the terminal run rspec --init
, which will create two things for you.
- A new directory named 'spec' which contains a file named
spec_helper.rb
- A new
.rspec
file in the root of the project directory.
.rspec
files contain the configuration setting for testing with RSpec. You can see all of the options with rspec --help
.
Here are a few things you can write in the .rspec
file to configure rspec:
-
--no-color
,--color
(--color
Shows green output for passing tests, red for failing tests) -
--format progress
,--format documentation
(--format progress
will show your test examples like this in the terminal: "....F..
" - Dots are passing, F is failed.--format documentation
will instead of showing dots and f's, will instead show the example descriptions of your tests in a green(passing) or red(failing) color. -
--no-profile
,--profile
(This one will determine whether or not RSpec shows you your fastest and slowest tests) -
--no-fail-fast
,--fail-fast
(--no-fail-fast
will run all tests,--fail-fast
will only output the first test that fails if any) -
--order defined
,--order random
(This one will either run the tests you have written in the order they are defined or at random)
If you create a .rspec
file in your computers user directory, you can configure RSpec globally there and the rest of your projects will use that configuration.
If you use a .rspec
file in the project directory, it will override any global .rspec
configurations for that project.
I have a Ruby class called Character
, which is loading a module that I am using for another class.
require_relative '../modules/instance_methods.rb'
class Character
include InstanceMethods
@@all = []
def self.all
@@all
end
end
Here is the module:
module InstanceMethods
attr_accessor(:name, :bio, :url, :start_i, :end_i)
def initialize(name, url)
@name = name
@url = url
self.class.all << self
end
end
Create a spec test for your class with the class name at the front, for example my spec file name is character_spec.rb
. In order to test the class from the spec file, you need to load the class code. At the top of your new spec file, require the file in which you are testing.
At the top of my spec file is:
require_relative '../lib/classes/character.rb'
There is a syntax RSpec uses to define tests.
1) The Example Group
describe Character do
# Example Cases go here
end
RSpec uses the describe
keyword which takes a string or class name argument followed by do
and end
to define an Example Group. Above I used a class name, but it could also be 'Character'
.
You can have two example groups inside of each other, in which case you can either use describe
or context
inside of the first example group because they are the same thing.
For example, you could set the first describe
for the Character class, and the second nested describe
for testing the attributes of the class.
Everything between that do
and end
belongs to the thing we want to test. In the code above, it is a class, so we are setting the Example Group to test the Character class.
2) Example Cases
describe Character do
it "Instantiate with a name & URL" do
#Expectations
end
end
The keyword it
followed by a string argument with do
and end
defines an Example Case. You can also use the synonym for it which is specify
. This is where you will house the actual test, the expectation. The string is what is outputted to you when you are testing your file just so you can see which test it is.
3) Expectations
it "Instantiate with a name & URL" do
@character = Character.new("Luffy", "https://onepiece.fandom.com/wiki/Monkey_D._Luffy")
expect(@character).to have_attributes(:name => "Luffy", :url => "https://onepiece.fandom.com/wiki/Monkey_D._Luffy")
end
This is where the actual test is. The syntax for expectations is as follows:
expect().to()
expect().not_to()
expect
will take an argument of the thing you want to test, you will call .to()
on the end of expect()
, then you will use something called a 'Matcher' as an argument to .to()
to test the two. There are many matchers:
- Observation matchers (How things change)
- collection matchers (Hashes, Arrays, Strings)
- numeric-comparison matchers (numbers)
- truthiness matchers (true || false)
- Composing Matchers (Matchers than can take other matchers as its argument)
- Equivalence matchers (Ruby loose, value & object identity equality)
- Predicate Matchers (Dynamic & Custom matchers)
You can go more in depth on the abundance of them here at:
Expectations & Matchers
To run your spec file, run in the terminal:
rspec
path-to-file-name.
To easily do this, type rspec
in the terminal with a space after it, drag and drop your spec file into the terminal and it will give the entire path to your file.
For example:
rspec /Users/user/Desktop/Flatiron/Projects/CLI/spec/character_spec.rb
Or you can simply just run in the terminal:
rspec spec
Which will run all tests in the spec directory.
Here is my finished spec for the Character class:
require_relative '../lib/classes/character.rb'
describe Character do
context "attributes" do
subject { Character.new("Luffy", "https://onepiece.fandom.com/wiki/Monkey_D._Luffy") }
it "Instantiate with a name & URL" do
expect(subject).to have_attributes(:name => "Luffy", :url => "https://onepiece.fandom.com/wiki/Monkey_D._Luffy")
end
it "Have attributes for :bio, :start_i, :end_i" do
expect(subject).to respond_to(:bio, :start_i, :end_i)
end
it ":name, :url & :bio must be strings" do
subject.bio = "Kaizoku-ō ni ore wa naru!"
expect(subject.name).to be_an(String)
expect(subject.url).to be_an(String)
expect(subject.bio).to be_an(String)
end
it ":start_i and :end_i must be integers" do
subject.start_i = 2
subject.end_i = 4
expect(subject.start_i).to be_an(Integer)
expect(subject.end_i).to be_an(Integer)
end
end
context 'class Methods' do
it '.all method which will record all instances of the class' do
expect(Character.all).to be_an(Array)
end
end
end
And here is the output for when I run my spec tests:
Character
attributes
Instantiate with a name & URL
Have attributes for :bio, :start_i, :end_i
:name, :url & :bio must be strings
:start_i and :end_i must be integers
class Methods
.all method which will record all instances of the class
Finished in 0.0073 seconds (files took 0.29535 seconds to load)
5 examples, 0 failures
Top comments (1)
"You can go more in depth on the abundance of them here at:
Expectations & Matchers" link not work