RSpec

Getting Started

Simplest Approach

Install rspec:

$ rvm install rspec

Then, if you have ruby specs like this one:

job.spec.rb
require_relative 'job'

describe Job do
  it 'should work' do
    expect(Job.new.run).to eq('Run!')
  end
end

Observe we do not need to require 'rspec'.

Simply run:

$ rspec --format=documentation ./job.spec.rb
rspec example fail

Update the implementation of Job#run to satisfy the test and run it again.

rspec example pass

We can replace --format=documentation with -f d. rspec --help says:

excerpt from rspec --help
-f, --format FORMATTER Choose a formatter.

  • [p]rogress (default - dots)
  • [d]ocumentation (group and example names)
  • [h]tml
  • [j]son
  • [f]ailures ("file:line:reason", suitable for
     editors integration)

Autorun

Sometimes when studying algorithms or doing some code algorithm and data structure challenges, it may be useful to simply have the tests on the same file as the implementation code itself.

First saw this approach used in the (free online) book How to Design Programs and the (free) courses UBCx: How to Code: Simple Data and UBCx: How to Code: Complex Data by Gregor Kiczales which are based on that awesome book.

With ruby and rspec, we can do it with this approach:

add.rb
require 'rspec/autorun'

class Calc
  class << self
    def add(x, y)
      x + y
    end
  end
end

describe 'Calc.add' do
  it 'should add two numbers' do
    expect(Calc.add(-1, 1)).to eq(0)
  end
end

Then run with ruby (not with rspec):

$ ruby ./add.rb
rspec autorun progress dots

Note by default rspec uses the progress (dots) output. Because we are using rspec’s autorun feature and we run the tests by using the ruby add.rb (instead of rspec add.rb), there is no way to pass rspec options on the command line like rspec --format documentation add.rb. Fortunatelly, rspec can read options from the .rspec config file:

path/to/project/.rspec
--color
--format documentation

Then it will show the test descriptions instead of simply the dots.

rspec autorun documentation

Assert STDOUT output

We want to assert output to STDOUT:

it 'should work' do
  expect { puts 'Aayla Secura' }.to output(/secura/i).to_stdout
end

With some class method that outputs to STDOUT:

describe PlainTextFormatter do
  it 'should output report title' do
    report = Report.new(PlainTextFormatter.new)

    expect do
      report.output
    end.to output(/===== Nostromo Report =====/).to_stdout
  end

  it 'should should output report body content' do
    report = Report.new(PlainTextFormatter.new)

    lines_to_match = [
      'This is Ripley, last survivor of the Nostromo.',
      'Signing off.'
    ].join("\n")

    expect { report.output }.to output(/#{lines_to_match}/).to_stdout
  end
end