Testing with Minitest
Prerequisites: Classes and objects, Gems and Bundler
A test is code that checks your other code works. Instead of running your program and eyeballing the output, you write down what should happen once, and a tool checks it every time you change something.
Minitest ships with Ruby, so it's the easiest place to start.
The code under test
# cart.rb
class Cart
attr_reader :items
def initialize
@items = []
end
def add(name, price)
raise ArgumentError, "price must be positive" unless price.positive?
@items << { name:, price: }
end
def total
@items.sum { |item| item[:price] }
end
end
{ name:, price: } is shorthand for { name: name, price: price }
(Ruby 3.1+).
A test file
# cart_test.rb
require "minitest/autorun"
require_relative "cart"
class CartTest < Minitest::Test
def setup
@cart = Cart.new
end
def test_starts_empty
assert_equal 0, @cart.total
assert_empty @cart.items
end
def test_total_adds_prices
@cart.add("book", 12)
@cart.add("pen", 3)
assert_equal 15, @cart.total
end
def test_rejects_non_positive_price
error = assert_raises(ArgumentError) { @cart.add("free", 0) }
assert_equal "price must be positive", error.message
end
end
$ ruby cart_test.rb
Run options: --seed 12345
# Running:
...
Finished in 0.000812s, 3694.5813 runs/s, 7389.1626 assertions/s.
3 runs, 6 assertions, 0 failures, 0 errors, 0 skips
Each . is a passing test.
- A test class inherits from
Minitest::Test; every method starting withtest_is a test. setupruns before each test, so every test gets a fresh cart.- Assertions check results:
assert_equal expected, actual,assert,refute,assert_nil,assert_includes,assert_raises. - Tests run in random order (the
--seed), which catches tests that accidentally depend on each other.
When a test fails
Minitest prints the difference between what you expected and what happened:
1) Failure:
CartTest#test_total_adds_prices [cart_test.rb:15]:
Expected: 15
Actual: 12
What to test
Test behavior, not implementation: what goes in and what comes out. Cover the normal case, edge cases (empty input, zero, the boundary of a range), and error cases.
Common mistakes
- Swapping the arguments: it's
assert_equal expected, actual. The wrong order makes failure messages misleading. - Tests that depend on each other's leftover state. Use
setupto start each test clean.