Rspec Cheatsheet
10 Apr 2025 - Gagan Shrestha
Mastering RSpec: A Comprehensive Guide to Testing in Ruby
RSpec is one of the most popular testing frameworks in the Ruby ecosystem, providing an expressive and powerful syntax for writing tests. This guide covers essential RSpec features including expectations, matchers, and test doubles to help you write effective and readable tests.
Basic Expectations
RSpec offers two syntaxes for writing expectations. The older should
syntax and the newer expect
syntax:
Should Syntax (Legacy)
1
2
target.should eq 1
target.should_not eq 1
Expect Syntax (Recommended)
1
2
expect(target).to eq 1
expect(target).not_to eq 1
The expect
syntax is now the recommended approach as it avoids extending core Ruby objects and provides more consistent behavior.
Object Expectations
Testing object types and capabilities:
1
2
3
4
5
6
7
8
# Verify exact class
expect(obj).to be_an_instance_of MyClass
# Verify inheritance hierarchy
expect(obj).to be_a_kind_of MyClass
# Verify method existence
expect(obj).to respond_to :save!
Numeric Expectations
RSpec provides numerous matchers for numeric comparisons:
1
2
3
4
5
6
7
8
9
10
# Basic comparisons
expect(5).to be < 6
expect(5).to == 5
expect(5).to equal value
# Range verification
expect(5).to be_between(1, 10)
# Delta comparison (floating point)
expect(5).to be_within(0.05).of value
Control Flow Expectations
Testing for exceptions and thrown symbols:
1
2
3
4
5
6
7
8
# Test for any exception
expect { user.save! }.to raise_error
# Test for specific exception and message
expect { user.save! }.to raise_error(ExceptionName, /msg/)
# Test for thrown symbols (less common)
expect { user.save! }.to throw :symbol
Compound Expectations
Combining expectations with logical operators:
1
2
# Combining expectations
expect(1).to (be < 2).or be > 5
Collection Expectations
Testing arrays and other enumerable objects:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Test for inclusion
expect(list).to include(<object>)
# Test collection size
expect(list).to have(1).things
expect(list).to have_at_least(2).things
expect(list).to have_at_most(3).things
# Test validation errors
expect(list).to have(2).errors_on(:field)
# Test exact contents (order-independent)
expect(list).to contain_exactly(1, 2)
expect(list).to match_array([1, 2])
Comparison Expectations
General-purpose comparison matchers:
1
2
3
4
5
6
7
8
# Identity comparison
expect(x).to be value
# Custom verification
expect(x).to satisfy { |arg| ... }
# Regular expression matching
expect(x).to match /regexp/
Predicate Expectations
RSpec automatically converts Ruby predicate methods into expectations:
1
2
3
expect(x).to be_zero # Uses FixNum#zero?
expect(x).to be_empty # Uses Array#empty?
expect(x).to have_key # Uses Hash#has_key?
Test Doubles
Test doubles (also known as mocks) allow you to create stand-ins for real objects in your tests:
Creating Doubles
1
2
3
4
5
# Basic double
book = double('book')
# Instance double (class-verified)
book = instance_double(Book, pages: 250)
Method Stubs
Setting up return values for methods:
1
2
3
4
5
6
7
8
# Basic stubbing
allow(die).to receive(:roll)
# Stubbing with return value
allow(die).to receive(:roll) { 3 }
# Stubbing any instance of a class
allow_any_instance_of(Die).to receive(:roll)
Method Expectations
Verifying that methods are called with specific arguments:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Basic expectation
expect(die).to receive(:roll)
# Argument matching
expect(die).to receive(:roll)
.with(1) # Exact match
.with(1, true) # Multiple arguments
.with(boolean) # Type matching
.with(anything) # Any object
.with(any_args) # Any arguments
.with(1, any_args) # First arg specific, rest anything
.with(no_args) # No arguments
.with(hash_including(a: 1)) # Hash with specific keys
.with(hash_excluding(a: 1)) # Hash without specific keys
.with(array_including(:a, :b)) # Array with specific elements
.with(array_excluding(:a, :b)) # Array without specific elements
.with(instance_of(Fixnum)) # Instance of exact class
.with(kind_of(Numeric)) # Instance of class or subclass
.with(<matcher>) # Any custom matcher
Call Count Expectations
Verifying how many times a method is called:
1
2
3
4
5
6
7
8
9
10
expect(die).to receive(:roll)
.once # Called exactly once
.twice # Called exactly twice
.exactly(n).times # Called exactly n times
.at_least(:once) # Called at least once
.at_least(:twice) # Called at least twice
.at_least(n).times # Called at least n times
.at_most(:once) # Called at most once
.at_most(:twice) # Called at most twice
.at_most(n).times # Called at most n times
Best Practices
- Prefer the
expect
syntax overshould
- Use descriptive context and example names
- Follow the “arrange, act, assert” pattern
- Test one behavior per example
- Use
let
for shared test data - Consider using
subject
for the object under test - Use
before
blocks sparingly - Test the public interface, not implementation details
Conclusion
RSpec provides a rich vocabulary for expressing test expectations in Ruby. By mastering these matchers and techniques, you can write tests that are both thorough and readable, serving as living documentation for your codebase.