ZetCode

Ruby Minitest

last modified October 18, 2026

Minitest is the standard testing library bundled with Ruby. It provides a small, fast API for writing unit tests: test classes inherit from Minitest::Test, test methods are named test_*, and assertions verify results. This guide shows how to write and run tests with Minitest, using a set of short examples that build from basic assertions to testing a class and reading failure output.

Note on output: the test runner prints a random seed and a timing line ("Finished in ...") that vary between runs. The examples below are run with --seed 42 so the order of tests is fixed. On your machine the timing numbers will differ, and the seed will differ if you omit the flag.

Basic Assertions

The require 'minitest/autorun' line loads Minitest, which ships with Ruby, so no gem installation is needed. Test cases subclass Minitest::Test, and every public method whose name starts with test_ is run as a separate test. This example uses assert, assert_equal, assert_nil, and refute.

ex1_basic.rb
#!/usr/bin/env ruby
require 'minitest/autorun'

class BasicTest < Minitest::Test
  def test_addition
    assert_equal 4, 2 + 2
  end

  def test_string_is_truthy
    assert "hello"
  end

  def test_nil_value
    assert_nil nil
  end

  def test_inequality
    refute 1 == 2
  end
end

assert_equal takes the expected value first and the actual value second. assert passes for any truthy value, assert_nil checks for nil, and refute is the negation of assert. The summary line reports the number of runs, assertions, failures, errors, and skips.

$ ./ex1_basic.rb --seed 42
Run options: --seed 42

# Running:

....

Finished in 0.001119s, 3574.2145 runs/s, 3574.2145 assertions/s.

4 runs, 4 assertions, 0 failures, 0 errors, 0 skips

Setup and Teardown

The setup method runs before each test method and the teardown method runs after it. Instance variables set in setup are available to every test, which avoids repeating the same fixture code. The puts calls make the order visible.

ex2_setup.rb
#!/usr/bin/env ruby
require 'minitest/autorun'

class SetupTest < Minitest::Test
  def setup
    @numbers = [3, 1, 2]
    puts "setup runs before each test"
  end

  def teardown
    puts "teardown runs after each test"
  end

  def test_sorted
    assert_equal [1, 2, 3], @numbers.sort
  end

  def test_size
    assert_equal 3, @numbers.size
  end
end

For each test, Minitest calls setup, then the test method, then teardown. Test methods run in a random order controlled by the seed, so tests should never depend on each other.

$ ./ex2_setup.rb --seed 42
Run options: --seed 42

# Running:

setup runs before each test
teardown runs after each test
.setup runs before each test
teardown runs after each test
.

Finished in 0.001057s, 1891.4998 runs/s, 1891.4998 assertions/s.

2 runs, 2 assertions, 0 failures, 0 errors, 0 skips

Testing a Class

This example tests a small Calculator class. The setup method creates a fresh instance for each test. The assert_raises assertion checks that a block raises the expected exception class.

ex3_calculator.rb
#!/usr/bin/env ruby
require 'minitest/autorun'

class Calculator
  def add(a, b)
    a + b
  end

  def divide(a, b)
    raise ZeroDivisionError, "cannot divide by zero" if b.zero?
    a / b
  end
end

class CalculatorTest < Minitest::Test
  def setup
    @calc = Calculator.new
  end

  def test_add
    assert_equal 5, @calc.add(2, 3)
  end

  def test_divide
    assert_equal 2, @calc.divide(10, 5)
  end

  def test_divide_by_zero
    assert_raises(ZeroDivisionError) { @calc.divide(1, 0) }
  end
end

assert_raises passes only when the block raises ZeroDivisionError; if nothing is raised, or a different exception is raised, the test fails. All three tests pass with seed 42.

$ ./ex3_calculator.rb --seed 42
Run options: --seed 42

# Running:

...

Finished in 0.001477s, 2030.6493 runs/s, 2030.6493 assertions/s.

3 runs, 3 assertions, 0 failures, 0 errors, 0 skips

Checking Collections and Strings

assert_includes checks whether a collection contains an element or a string contains a substring. Here it is used on an array and on a string, both of which respond to include?.

ex4_strings.rb
#!/usr/bin/env ruby
require 'minitest/autorun'

class StringUtilsTest < Minitest::Test
  def test_includes_element
    assert_includes %w[ruby python go], "ruby"
  end

  def test_includes_substring
    assert_includes "hello world", "world"
  end
end

Both tests pass, giving 2 runs and 4 assertions in total. The summary line reports the counts; the dot on each line of the progress row marks a passing test.

$ ./ex4_strings.rb --seed 42
Run options: --seed 42

# Running:

..

Finished in 0.000950s, 2104.4701 runs/s, 4208.9402 assertions/s.

2 runs, 4 assertions, 0 failures, 0 errors, 0 skips

A Failing Assertion

This test is intentionally wrong: 2 + 2 is 4, not 5. Running it shows how Minitest reports a failure, with the test name, file and line, and the expected and actual values.

ex5_failing.rb
#!/usr/bin/env ruby
require 'minitest/autorun'

class FailingTest < Minitest::Test
  def test_wrong_sum
    assert_equal 5, 2 + 2
  end
end

The F in the progress row marks the failure. The process exits with status 1, which is what a CI system uses to detect failing tests. The file and line in the failure header point to the failing assertion.

$ ./ex5_failing.rb --seed 42
Run options: --seed 42

# Running:

F

Finished in 0.000933s, 1072.0251 runs/s, 1072.0251 assertions/s.

  1) Failure:
FailingTest#test_wrong_sum [ex5_failing.rb:6]:
Expected: 5
  Actual: 4

1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

List all Ruby tutorials.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.