p.s. This is a series of articles and each article builds off another. I suggest starting at Part 1.
Mini Series
- TestUnit - Writing Test Code In Ruby (1/3)
- MiniTest - Writing Test Code In Ruby (2/3)
- RSpec - Writing Test Code In Ruby (3/3)
MiniTest
MiniTest is similar to UnitTest in that is has assertion functions
in the style of xUnit/TDD. This style we are referring to is the fact that
our assertion functions all start with assert_
MiniTest has more built into tools that make it easier to manage and run
tests.
MiniTest is also capable of spec/BDD style tests which will talk about later.
Here are the various promises that MiniTest provides out of the box:
minitest/autorun - the easy and explicit way to run all your tests.
minitest/test - a very fast, simple, and clean test system.
minitest/spec - a very fast, simple, and clean spec system.
minitest/mock - a simple and clean mock/stub system.
minitest/benchmark - an awesome way to assert your algorithm's performance.
minitest/pride - show your pride in testing!
MiniTest is not a standard library.
To install MiniTest we will need to install the gem.
Gem is what ruby calls a library.
Gems are commonly managed using bundler which is a package manager.
We are going to be using bundler in all future lectures.
Setting Up Bundler and Installing MiniTest
Install Bundler
To start using bundler we must install the bundler gem via rubygems.
gem install bundler
Create Gemfile
Once bundler is installed we can now create a Gemfile
.
A Gemfile
defines all the gems our code will need to depend on.
We can specify exact gem versions and bundler call automate the
installation and update of these gems.
We will proceed to create our Gemfile
# Gemfile
source 'https://rubygems.org'
git_source(:github) do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
"https://github.com/#{repo_name}.git"
end
gem 'minitest'
In our Gemfile
we have to specify where we want to get our gems.
So here we say we want to download our gems from rubygems.org
source 'https://rubygems.org'
In order to pull in gems from github we need to add a hack.
In the past this hack was not required but is now in the norm for all ruby projects.
git_source(:github) do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
"https://github.com/#{repo_name}.git"
end
Then we can list the gems we want to install, So here is where we
include minitest
gem 'minitest'
Bundle Install
To install the minitest
gem via bundler we need to run in terminal:
bundle install
Bundler will proceed to install or update gems and produce a
Gemfile.lock
which is known as a lockfile.
Lockfile
The lockfile is to lock down specific gem versions in order to ensure
others who want to run our code don't run into errors caused by using
the wrong versions of gems.
You can not manually modify the lockfile.
Turning our UnitTest into MiniTest
We are going to test our Hello
class from the last lecture.
# hello.rb
class Hello
def self.world
'world2'
end
end
We previously wrote a HelloTest
class using UnitTest
.
Let us convert it to use MiniTest
and compare the differences.
require 'minitest/autorun'
require_relative './hello'
class HelloTest < Minitest::Test
def test_world
assert_equal 'world', Hello.world, "Hello.world should return a string called 'world'"
end
def test_flunk
flunk "You shall not pass"
end
end
We only changed two things the require:
require 'minitest/autorun'
and the class which we are extending.
class HelloTest < Minitest::Test
MiniTest
is very close in DSL syntax and assertion function naming to UnitTest
.
This is no coincidence as it was intended to help developers to port their UnitTest
code to MiniTest
Running MiniTest
Since we are using bundler to managed our gems when we run our test file
we need it to perform it in the context of bundler. We can do this by
using bundle exec
. This is how we would run our test:
bundle exec ruby hello_test.rb
Assertion Functions
We looked at the UnitTest
assertion functions previously. Lets see how they compare to MiniTest
:
assert
assert_empty
assert_equal
assert_in_delta
assert_in_epsilon
assert_includes
assert_instance_of
assert_kind_of
assert_match
assert_mock
assert_nil
assert_operator
assert_output
assert_predicate
assert_raises
assert_respond_to
assert_same
assert_send
assert_silent
assert_throws
capture_io
capture_subprocess_io
diff
exception_details
flunk
message
mu_pp
mu_pp_for_diff
pass
refute
refute_empty
refute_equal
refute_in_delta
refute_in_epsilon
refute_includes
refute_instance_of
refute_kind_of
refute_match
refute_nil
refute_operator
refute_predicate
refute_respond_to
refute_same
skip
skipped?
Many of the same assertion functions exist and there are new functions that did not exist prior.
MiniTest
is not 1-to-1 the same as UnitTest
.
So Many Extensions
MiniTest has hundred of extensions in the form of gems.
This is one advantage over UnitTest
since you can customize your testing framework to your needs.
Here are a few examples:
minitest-line - Run test at line number.
minitest-logger - Define assert_log and enable minitest to test log messages. Supports Logger and Log4r::Logger.
minitest-osx - Reporter for the Mac OS X notification center.
minitest-profile - List the 10 slowest tests in your suite.
minispec-rails - Minimal support to use Spec style in Rails 5+.
MiniTest's alternative spec/BDD syntax
So far we have been using Test Drive Development (TDD) which is a
methodology for writing test code. There is another newer way of writing
tests (which are called) using Behaviour Driven Developement (BDD).
The goal of BDD is to define tests that are both more human readable and
written in such a way that it's more likely to cover all edge cases.
Let us convert our TDD MiniTest over to BDD.
# hello_spec.rb
require 'minitest/autorun'
require_relative './hello'
describe Hello do
describe "#world" do
it "should return world" do
Hello.world.must_equal 'world'
end
end
end
BBD uses a heavy DSL using ruby blocks function value do
.
The assertion functions are both used and named differently.
assert_equal
has now become must_equal
The assertion function now uses a syntax known as chaining.
These changes are intended to make the test more human readable.
Let us compare:
# TDD "test"
assert_equal 'world', Hello.world, "Hello.world should return a string called 'world'"
# BDD "spec"
it "should return world" do
Hello.world.must_equal 'world'
end
We can run this spec file just as we did our test:
bundle exec ruby hello_spec.rb
Thoughts on MiniTest
MiniTest
can handle both TDD and BDD syntax.
It comes with a bunch of testing tools to make automation easy
There are several extensions to customize MiniTest
to your needs.
It is fairly easy to port TestUnit
to MiniTest
.
MiniTest
does have to be installed via bundler.
In the next lecture, we are going to look at RSpec which is solely a BDD tool.
Code
References
https://mattbrictson.com/minitest-and-rails
https://github.com/seattlerb/minitest
http://docs.seattlerb.org/minitest/Minitest/Assertions.html
Top comments (4)
Hey! just read this series and it's super interesting, thanks! however, in this minitest part, you wrote that:
and I was surprised, because MiniTest is actually bundled with ruby by default, I looked into it and it's apparently the case since Ruby 2.2 (stdgems.org/minitest/). Or am I missing something here? :)
This is really cool, thank you! Can anyone point out a few resources that go through the mental models of testing with assertions and/or specs?
I did find a few guides on how the syntax works and available methods but less on the reasoning behind figuring out what to test and how. Any tips or resources would be super helpful, thank you!
hey @cristiano , this material may interest you: tdd-ruby.gitbook.io/book
Isn't a new syntax required for minitest spec?
_(@foo).must_equal 100
Which IMHO is really ugly. I used to prefer minitest spec but I think I am going back to plain old minitest.