Makers Academy Journal

Test-Driven Development in Ruby: Using RSpec For Feature & Unit Tests

Posted by Adam

RSpec is a framework used in test-driven development (TDD) for writing feature and unit tests to evaluate whether a computer program is fit for purpose.

Test-Driven Development works in a red-green loop:

  • Write the smallest possible test case that matches what we need to program.
  • Run the test and watch it fail. This gets you into thinking how to write only the code that makes it pass.
  • Write some code to make the test pass.
  • Run your test suite. Repeat steps 2 and 3 until all tests pass.
  • Go back and refactor your new code, making it as simple and clear as possible while keeping the test suite green.

The test always fails the first time because when we write the test, we haven’t attempted to pass it yet.

RSpec can be installed by navigating to the root project folder and typing:

$ gem install rspec

Feature & Unit Tests

Unit tests test individual parts of the logic of a program. When you write a unit test, it should be as small and simple as possible. A feature test tests the program as a user. e.g. using the interface a user would use to see if the feature works.

Guide To RSpec

By convention, tests written with RSpec are called “specs” (short for “specifications”) and are stored in the project’s spec directory at root/spec/.

With RSpec, we are always describing the behaviour of classes, modules and their methods. The describe block is always used at the top to put specs in a context. It can accept either a class name, in which case the class needs to exist, or any string you’d like.

describe ClassName do
end

This is perhaps the simplest RSpec test. This test will simply test for the existence of ClassName.

If we run the test now, it will fail because we haven’t written any code yet. This is in fact what we expect, so it’s not a bad thing.

We will get a uninitialized constant error because we have asked the test to check for the existence of a constant called ClassName, but as we have no production code yet, this constant cannot possibly exist – because nothing exists yet.

All we have to do to get this test to pass is to initialise the constant by calling it in the production code (stored by convention in the lib folder):

class ClassName
end

Before we run the test again, we need to alert Rspec of the existence of the file in which we’ve called the constant.

At the top of the spec file (above the test), add the line:

require 'filename'

Note: the file extension (.rb) and the relative file path (root/lib/) are assumed by Rspec.

The test should now pass.

More Tests…

Let’s look at a slightly more complex example…

describe StringCalculator do

  describe ".add" do
    context "given an empty string" do
      it "returns zero" do
        expect(StringCalculator.add("")).to eq(0)
      end
    end
  end
end

Notes about this example:

  • We are using another describe block to describe the add class method. By convention, class methods are prefixed with a dot (".add"), and instance methods with a dash ("#add").
  • We are using a context block to describe the context under which the add method is expected to return zero. context is technically the same as describe, but is used in different places, to aid reading of the code.
  • We are using an it block to describe a specific example, which is RSpec’s way to say “test case”. Generally, every example should be descriptive, and together with the context should form an understandable sentence. This one reads as “add class method: given an empty string, it returns zero“.
  • expect(...).to and the negative variant expect(...).not_to are used to define expected outcomes. The Ruby expression they are given (in our case, StringCalculator.add("")) is combined with a matcher to fully define an expectation on a piece of code. The matcher we are using here is eq, a basic equality matcher. RSpec comes with many more built-in matchers.

RSpec Test Template

describe ClassName do
  describe "methodname" do
    expect(Classname.methodname($args)).to eq(result)
  end
end

RSpec One-Liner Syntax

Rspec tests can also be written using one-liner syntax. There is surprisingly little literature about this online.

I need to figure this out.

to complete

Leave A Comment