DEV Community

Cover image for MiniTest - Writing Test Code In Ruby (2/3)
Andrew Brown 🇨🇦 for ExamPro

Posted on

MiniTest - Writing Test Code In Ruby (2/3)

p.s. This is a series of articles and each article builds off another. I suggest starting at Part 1.

Mini Series

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!
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then we can list the gems we want to install, So here is where we
include minitest

gem 'minitest'
Enter fullscreen mode Exit fullscreen mode

Bundle Install

To install the minitest gem via bundler we need to run in terminal:

bundle install
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

We only changed two things the require:

require 'minitest/autorun'
Enter fullscreen mode Exit fullscreen mode

and the class which we are extending.

class HelloTest < Minitest::Test
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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?
Enter fullscreen mode Exit fullscreen mode

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+.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

We can run this spec file just as we did our test:

bundle exec ruby hello_spec.rb
Enter fullscreen mode Exit fullscreen mode

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

Latest comments (3)

Collapse
 
pabuisson profile image
Pierre-Adrien Buisson

Hey! just read this series and it's super interesting, thanks! however, in this minitest part, you wrote that:

MiniTest is not a standard library.

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? :)

Collapse
 
cristiano profile image
cristiano

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!

Collapse
 
pitosalas profile image
Pito Salas

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.