Matthew Lang avatar

Minitest

Minitest ships in Ruby’s standard library and is Rails’ default testing framework. It supports two styles — classic Test::Unit-style assertions, and a spec-style DSL — that can be mixed freely.

Assertion style

require "minitest/autorun"

class PostTest < Minitest::Test
  def setup
    @post = Post.new(title: "Hello")
  end

  def test_title_is_present
    assert @post.valid?
    assert_equal "Hello", @post.title
  end

  def teardown
    # runs after every test
  end
end

Spec style

describe Post do
  before { @post = Post.new(title: "Hello") }

  it "is valid with a title" do
    _(@post).must_be :valid?
  end
end

Spec style is just sugar over the same assertion methods (must_equalassert_equal, etc.) — pick one per project and stay consistent.

Common assertions

Assertion Checks
assert x x is truthy
refute x x is falsy
assert_equal exp, act exp == act
assert_nil x x.nil?
assert_includes coll, x coll includes x
assert_raises(ErrClass) { ... } block raises ErrClass
assert_instance_of Klass, x x.instance_of?(Klass)
assert_respond_to x, :foo x responds to :foo
assert_in_delta exp, act, delta numeric closeness

Every assert_* has a refute_* counterpart.

Rails integration

class PostTest < ActiveSupport::TestCase
  test "requires a title" do
    post = Post.new
    assert_not post.valid?
  end
end
  • ActiveSupport::TestCase gives you the test "description" do ... end macro (defines a method under the hood) and loads fixtures automatically.
  • Fixtures live in test/fixtures/*.yml, referenced as posts(:one) in tests.
  • Controller/request tests: ActionDispatch::IntegrationTest, with get, post, assert_response :success, assert_redirected_to.
  • System tests (real browser via Capybara): ApplicationSystemTestCase.

Mocking & stubbing

mailer = Minitest::Mock.new
mailer.expect :deliver, true
UserMailer.stub :welcome, mailer do
  # code that calls UserMailer.welcome
end
mailer.verify
  • obj.stub(:method, return_value) { ... } — temporarily replaces a method for the block’s duration.
  • Minitest::Mock#expect(name, retval, args = []) then .verify to assert it was called as expected.
  • Prefer real objects and fixtures where practical; reach for mocks mainly at slow/external boundaries (network calls, mailers, third-party APIs).

Running tests

rails test                       # whole suite
rails test test/models/post_test.rb
rails test test/models/post_test.rb:12   # single test by line number
rails test:models / :controllers / :system
ruby -Itest test/models/post_test.rb     # plain Ruby project, no Rails
  • Parallel test execution is on by default in Rails (parallelize(workers: :number_of_processors) in test_helper.rb).
  • bin/rails test --seed 1234 reproduces a specific run order (Minitest randomizes by default).

Assertions vs expectations

Minitest deliberately keeps the API small compared to RSpec’s large matcher library. If you want richer custom matchers, minitest/spec plus a handful of custom Minitest::Assertions methods usually covers it without pulling in a bigger DSL.