Testing with Ruby is often done with RSpec, but Minitest is the default testing library. Functionality wise, both can do the same things but Minitest has a more straight forward (Ruby-like) syntax while RSpec is closer to natural English. Rspec is a Domain Specific Language (DSL).
Vocabulary of Testing
- Test Suite: the entire set of test for a program.
- Test: the situation or context in which tests are run. A test can contain multiple assertions.
- Assertion: the actual verification step; what is done to confirm that the data returned is what was expected.
Autopsy of a Test File
Assuming a Flamingo
class with an instance variable wings
which points to integer object 2
, we can write the following test:
require 'minitest/autorun' # loads files from `minitest` gem
require_relative 'flamingo' # name of the file we're testing
# `require_relative` means from the
# current file's directory
class FlamingoTest < MiniTest::Test # need to be a subclass of
# `MiniTest::Test` to inherit test
# methods
def test_wings # each test has its own instance method
# that starts with `test_`
flamingo = Flamingo.new
assert_equal(2, flamingo.wings) # the actual assertion
end
end
Test Output
On our above file, the output would be something like this:
Run options: --seed 57527
# Running:
.
Finished in 0.000684s, 1461.4371 runs/s, 1461.4371 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips
The dot .
represents one successfully ran test. When skipped or failed, this dot became a S
or a F
, respectively.
seed
tells the order the tests were run in. With multiple tests run in random order, having a seed can help reproduce a bug which only happens when tests are run in a particular order. To rerun the test in the same order, we can append --seed 57527
to our command.
Assertions
The example above used a straight forward assertion: the one for equality. There are other type of assertion, actually too much to list them all. They can be seen on this page.
The most common ones are written below:
-
assert(test)
: test for truthiness -
assert_equal(exp, act)
: test for equality ofexp
andact
-
assert_nil(obj)
: test fornil
-
assert_raises(*exp) { ... }
: test for block raise of*exp
-
assert_instance_of(cls, obj)
: test for instance of classcls
-
assert_includes(collection, obj)
: test for inclusion ofobj
incollection
Most assert
(and refute
, see below) methods accept a message argument, with an exception for assert_mock
, assert_raises
and assert_silent
.
Refutations
Refutation are the opposite of assertions: they refute instead of assert. So for example, refute(test)
check for falsiness, refute_nil
test for not nil
etc.
Setup and Teardown
To perform or run actions before or after each test, Minitest provides both setup
and teardown
methods. Each class that defines a test suite can have those two methods.
See also the SEAT Approach.
setup
is often used to perform setup and requirements before each test. teardown
is called for any required cleanup. This is often use to connect/disconnect to a database, for example.
Both methods are optional and independent: we can have both, neither or one of the two.
class DatabaseTest < Minitest::Test
def setup
@myapp = MyApp.new
end
def test_something
# perform some test
end
def test_something_else
# perform some test
end
def teardown
@myapp.cleanup
end
end
Prior to test_something
and test_something_else
, setup
creates a @myapp
instance variable. After each test, teardown
calls @myapp.cleanup
which does whatever is useful for MyApp
class.