Testing with RSpec
describe, context, it, subject, let (lazy), let! (eager), before/after/around hooks
eq, eql, equal, be, be_nil, be_truthy, include, match, start_with, raise_error, change, output, contain_exactly, match_array, have_attributes, be_within, satisfy, predicate matchers (be_empty → empty?)
double, instance_double (verified), allow(...).to receive(...), expect(...).to receive(...), and_return, and_raise, spies with have_received
shared_examples, it_behaves_like, shared_context :focus, :slow, tags, --tag, pending, skip
.rspec, spec_helper.rb, --format documentation, random order (--order random)
Ecosystem: FactoryBot (test data), Faker, WebMock/VCR (HTTP stubbing/recording), Capybara (browser testing), SimpleCov (coverage), Timecop (time travel, or ActiveSupport's travel_to), mutant (mutation testing), Cucumber (BDD with Gherkin).
# spec/bank_account_spec.rb
require "bank_account"
RSpec.describe BankAccount do
subject(:account) { described_class.new("Ana", 100) }
describe "#deposit" do
it "increases the balance" do
expect { account.deposit(50) }.to change(account, :balance).by(50)
end
context "with a negative amount" do
it "raises an error" do
expect { account.deposit(-5) }.to raise_error(ArgumentError, /positive/)
end
end
end
describe "#withdraw" do
it "returns false when funds are insufficient" do
expect(account.withdraw(500)).to be false
expect(account.balance).to eq(100)
end
end
describe "notifications" do
let(:notifier) { instance_double("Notifier", notify: true) }
it "notifies on large deposits" do
account = described_class.new("Bo", 0, notifier:)
account.deposit(10_000)
expect(notifier).to have_received(:notify).with(/large deposit/)
end
end
it "matches complex structures" do
expect(account.to_h).to include(owner: "Ana", balance: a_value > 0)
expect([1, 2, 3]).to contain_exactly(3, 1, 2)
expect("hello").to start_with("he").and end_with("lo")
end
end