RSpec in one lesson

Installing RSpec

RSpec is a gem with a few supporting gems; the install is one command and the runner is rspec:

gem install rspec

6 gems installed

A spec file

A spec file lives in spec/ and ends in _spec.rb. describe names the unit, it names a behaviour and expect(...).to is the assertion:

require_relative "../lib/calculator" RSpec.describe Calculator do it "adds" do expect(Calculator.new.add(2, 2)).to eq(4) end it "raises on zero" do expect { Calculator.new.divide(1, 0) } .to raise_error(ArgumentError, "division by zero") end end

eq is equality, raise_error takes a block because the call must not run before the matcher. The matcher list is longer than Minitest's: include, match, be_between, change, output.

Running it

rspec

.. Finished in 0.00633 seconds (files took 0.17427 seconds to load) 2 examples, 0 failures

rspec without an argument reads spec/. A single file is rspec spec/calc_spec.rb, and a single example is the file plus a line number: rspec spec/calc_spec.rb:10.

The same test in Minitest

The two tests, side by side. The behaviour is identical; the words differ:

RSpecMinitest spec style
expect(x).to eq(4)_(x).must_equal 4
expect { f }.to raise_error(E)_(-> { f }).must_raise E
described_classCalculator or let
before { }before { } in a describe block

RSpec's output names each example as it fails, which is the feature its users miss in Minitest. Minitest is in the standard toolchain of every Ruby and starts in milliseconds, which is the feature its users miss in RSpec. Both are fine; one per project is the rule.

What to watch for

expect without .to asserts nothing and passes. The line reads as an assertion and is a no-op, which is the quietest way to lose a test.

RSpec loads spec/spec_helper.rb only when the file requires it. Configuration put there and never required has no effect, and the run looks fine.

Next: Testing files and IO

Article author: Arthur Isaev