Koren Leslie Cohen

  • About
  • Blog
  • Contact

0 comments

Ruby / Rails, TDD, Tutorials

A Beginner’s Guide To RSpec

June 24, 2014 by Koren Leslie Cohen

devise redirects

RSpec is a behavior-driven testing tool for the Ruby language.  RSpec is used in test driven development (“TDD”), where the developer writes the test based on what s/he hopes the program will accomplish, and then creates the program to accomplish the goals of the test.

While this would seem to create additional work for the developer, it actually saves time in the development process as the developer is more easily able to tell whether the program is functioning properly.

Installing RSpec

We’ll start by making sure you have rspec installed.  If you don’t, you can easily install rspec  with gem install rspec.

Setting Up Our Directory and Test Suite

To begin, we’ll create a directory called school  using mkdir school.  We’ll then migrate to our school directory and create a file called student.rb using touch student.rb at the command line.

It’s now time for us to create our test suite.  To do this, we’ll create a directory inside the school directory called spec by running mkdir spec.  Inside the spec directory, we’ll create two files: student_spec.rb and spec_helper.rb using touch student_spec.rb and touch spec_helper.rb.

There should be one spec file per class.  However, even with multiple classes, we only need one spec_helper, as the spec_helper exists to require the Ruby classes we’re testing.

spec_helper.rb

Inside our spec_helper file, we need to require our Ruby classes:

require_relative ‘../student’

require_relative is used to require a directory or file relative to the current directory.

student_spec.rb

In student_spec, we’ll create the tests needed to ensure our Ruby code in student.rb is running properly.  The first thing we’ll want to do is require the spec_helper with require_relative ‘spec_helper.rb’.  We’ll then want to create a shell for our specs.  Our file thus far will look something like:

require_relative 'spec_helper.rb'

describe Student do

  # tests  

end

describe simply describes what we’re testing which, in this case, is the class Student.

Ideally, we’d like a program that will keep track of all students at our school. To do this, we want our program to be able to initialize a student, save the student’s name so it cannot be changed, and be able to list all of the students.  To do this, our code will look something like:

require_relative 'spec_helper.rb'

describe Student do

  before(:each) do
    Student.reset_all
  end

  let(:student) { Student.new("Koren") }

  it "can initialize a student" do
    expect(student).to be_a(Student)
  end

  it "initializes with a name" do
    expect(student.name).to eq("Koren")
  end

  it "can't change its name" do 
    expect { student.name = "Koren" }.to raise_error
  end

  it "can count how many students have been created" do
    Student.new("student")
    expect(Student.count).to eq(1)
  end 

end

Let’s break down the above code:  We are describing the student class, and the things we’d like the class to do.  Each time we run rspec, we want our class to reset itself with a method called reset_all.  This is because each time we run rspec we will create a new instance of the class, and our tests will be compromised.

After the reset, we want our class to accomplish 4 things: (1) it can initialize a student, (2) it can initialize a student with a name, (3) it should not allow a student’s name to be changed, and (4) it should keep track of how many students have been initialized.  Of course, due to the reset, this will always be 1 when we run rspec, though this just ensures the program is functioning properly.  In reality, there will likely be many more instances of the student class.

Running RSpec

When we first run rspec, we are unable to see our examples and errors.  Instead we get the following error:

/Users/korenlcohen/Flatiron/school/spec/students_spec.rb:3:in `<top (required)>': uninitialized constant Student (NameError)
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/gems/rspec-core-3.0.2/lib/rspec/core/configuration.rb:1057:in `load'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/gems/rspec-core-3.0.2/lib/rspec/core/configuration.rb:1057:in `block in load_spec_files'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/gems/rspec-core-3.0.2/lib/rspec/core/configuration.rb:1057:in `each'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/gems/rspec-core-3.0.2/lib/rspec/core/configuration.rb:1057:in `load_spec_files'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/gems/rspec-core-3.0.2/lib/rspec/core/runner.rb:97:in `setup'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/gems/rspec-core-3.0.2/lib/rspec/core/runner.rb:85:in `run'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/gems/rspec-core-3.0.2/lib/rspec/core/runner.rb:70:in `run'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/gems/rspec-core-3.0.2/lib/rspec/core/runner.rb:38:in `invoke'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/gems/rspec-core-3.0.2/exe/rspec:4:in `<top (required)>'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/bin/rspec:23:in `load'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/bin/rspec:23:in `<main>'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/bin/ruby_executable_hooks:15:in `eval'
	from /Users/korenlcohen/.rvm/gems/ruby-2.1.2/bin/ruby_executable_hooks:15:in `<main>'

As you can see, this error says we have an uninitialized constant Student: /Users/korenlcohen/Flatiron/school/spec/students_spec.rb:3:in `<top (required)>’: uninitialized constant Student (NameError) .  To correct this error, we simply need to create a student class in student.rb:

class Student

end

Once our class has been created, rspec will run properly:

Failures:

  1) Student can initialize a student
     Failure/Error: Student.reset_all
     NoMethodError:
       undefined method `reset_all' for Student:Class
     # ./spec/students_spec.rb:6:in `block (2 levels) in <top (required)>'

  2) Student initializes with a name
     Failure/Error: Student.reset_all
     NoMethodError:
       undefined method `reset_all' for Student:Class
     # ./spec/students_spec.rb:6:in `block (2 levels) in <top (required)>'

  3) Student can't change its name
     Failure/Error: Student.reset_all
     NoMethodError:
       undefined method `reset_all' for Student:Class
     # ./spec/students_spec.rb:6:in `block (2 levels) in <top (required)>'

  4) Student can count how many students have been created
     Failure/Error: Student.reset_all
     NoMethodError:
       undefined method `reset_all' for Student:Class
     # ./spec/students_spec.rb:6:in `block (2 levels) in <top (required)>'

Finished in 0.00104 seconds (files took 0.15203 seconds to load)
4 examples, 4 failures

Failed examples:

rspec ./spec/students_spec.rb:11 # Student can initialize a student
rspec ./spec/students_spec.rb:15 # Student initializes with a name
rspec ./spec/students_spec.rb:19 # Student can't change its name
rspec ./spec/students_spec.rb:23 # Student can count how many students have been

As you can see, we have 4 examples and 4 failures.  Assuming you know simple Ruby, you’ll be able to make these tests pass quickly with the following code:

class Student
  attr_reader :name

  @@students = []

  def initialize(name)
    @name = name
    @@students << self
  end

  def self.count
    @@students.count
  end

  def self.reset_all
    @@students.clear
  end

end

If you run the test now, we’ll have 0 errors.  Of course, as your programs become more complicated and must accomplish a greater number of tasks, your test suite will become comparably more complicated.

RSpec Tips

When you’re working with a test suite that contains multiple examples, sometimes it helps to streamline the process by using rspec –fail-fast.  This will generate a single failure at a time, so you can easily zero in on an error instead of scrolling through multiple failures at the same time.

If you want your examples and failures to appear in color, you can use rspec -c.

  • About
  • Latest Posts
Connect
Koren Leslie Cohen
Product manager at Facebook. Former senior product manager at Dollar Shave Club in Los Angeles and software engineer at J.Crew / Madewell in New York City. Recovering trial lawyer.
Connect
Latest posts by Koren Leslie Cohen (see all)
  • PM Career Story - April 28, 2022
  • How to Transition into Product Management - December 26, 2017
  • What I’ve Learned in My First Few Months as a Product Manager - October 14, 2015

Related Posts

FizzBuzz in Ruby (and the Strangeness of Ruby Loops)
Rails: Generate Model vs. Resource vs. Scaffold

Share

Facebook Google+ Twitter Pinterest Email

Comments Cancel reply

Your email address will not be published. Required fields are marked *

*

code

Back to Blog

  • GitHub
  • Instagram
  • LinkedIn
  • RSS
  • Twitter

Looking for something?

Copyright 2023 Koren Leslie Cohen