Minitest assertions
The class under test
A test needs something with a rule worth checking. The calculator below has one normal path and one error path:
class Calculator
def add(a, b) = a + b
def divide(a, b)
raise ArgumentError, "division by zero" if b.zero?
a / b
end
end
gem install minitest
2 gems installed
assert and refute
A test class inherits from Minitest::Test, and every method that starts with test_ is one test. assert_equal takes the expected value first, which is what the failure message prints:
require "minitest/autorun"
require_relative "../lib/calculator"
class CalculatorTest < Minitest::Test
def test_add
assert_equal 4, Calculator.new.add(2, 2)
end
def test_not_nil
refute_nil Calculator.new
end
end
refute is assert with the condition inverted, and refute_nil is the common case with a better message. The one-assertion rule is a guideline: a test with five assertions fails on the first and hides the others, so each test names one behaviour.
assert_raises
The error path is a test too. assert_raises takes the class, runs the block and returns the exception, so the message can be checked as well:
def test_divide_by_zero
error = assert_raises(ArgumentError) { Calculator.new.divide(1, 0) }
assert_equal "division by zero", error.message
end
Without assert_raises the exception escapes and the test is reported as an error, not a failure. The difference matters: a failure is a wrong value, an error is code that did not return.
setup and teardown
setup runs before every test and teardown after it, whatever the test did. The instance variable is fresh for each test, which is what stops one test from depending on another:
class CalculatorTest < Minitest::Test
def setup
@calc = Calculator.new
end
def teardown
@calc = nil
end
def test_add
assert_equal 4, @calc.add(2, 2)
end
end
Running it
ruby test/calc_test.rb
... Finished in 0.001132s, 2649.860 runs/s, 3533.147 assertions/s. 3 runs, 4 assertions, 0 failures, 0 errors, 0 skips
Three tests, four assertions: the error test makes two. Every line of the summary is read by a person or a parser, and the exit status is zero only when failures and errors are both zero.
A single test is run by name: ruby test/calc_test.rb -n test_add, and a whole suite by a glob: ruby -Itest test/*_test.rb.
What to watch for
A test that asserts the current output of a bug is a test that protects
the bug. Write the expected value from the rule, not from a run.
Random order is a feature: a suite that only passes in one order has a
test that changes shared state. Minitest shuffles per run, and the seed
in the output is the way to repeat a failing order.
Next: Testing a web app
Article author: Arthur Isaev