Make Your Ruby Tests Cleaner with Minitest and Shoulda

Screenshot 2014-06-15 10.38.59

Back before RSpec and Cucumber, a core library in Ruby called Test Unit existed. It was simply a framework for unit testing code. Ever since Ruby 1.9.3, however, Test Unit has been replaced by Minitest as the included testing framework for Ruby. In this article, we’re going to overview Minitest (as it has now superceded Test Unit) and the benefits of shoulda-context, a Test::Unit/Minitest framework.

Prerequisites

  • Ruby (duh; current 2.1.2 would be best)
  • The ability to run gem install GEM without a hassle.
  • A small amount of TDD knowledge, with a sprinkle of BDD.

Test driven development (TDD) has always been a fixture in the Ruby community. It is a core concept in extreme programming and is used in virtually every Ruby project. Making unit tests with Minitest is simple, but first, we need a Ruby library to test. Our final project structure will look like this:

.
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── lib
│   └── calculator.rb
└── test
    ├── test_calculator.rb
    ├── test_calculator_basic.rb
    └── test_helper.rb

You can find the repo for this project here.

Our library is a calculator with two methods: add and subtract.

class Calculator
  def add(num1, num2)
    num1 + num2
  end

  def subtract(num1, num2)
    num1 - num2
  end
end

To test this library with Minitest, create a test class that inherits from Minitest::Test.

# file: test/test_calculator_basic.rb
  require 'minitest/autorun'
  require 'calculator' # don't worry, this works

  class TestCalculator < Minitest::Test

  end

Tests inside of this class are methods that start with test_ and should describe what the test is testing.

# file: test/test_calculator_basic.rb

require 'minitest/autorun'
require 'calculator'

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

  def test_proper_addition
    assert_equal 4, @calc.add(2, 2)
  end

  def test_proper_subtraction
    assert_equal 0, @calc.subtract(2, 2)
  end
end

To run this test, use the following command: $ ruby -I test:lib test/test_calculator_basic.rb. This may seem a little repetetive, especially if you plan on using a CI server, so we can use a simple Rake task for automation.

# file: Rakefile
require 'rake/testtask'

Rake::TestTask.new do |task|
  task.libs << %w(test lib)
  task.pattern = 'test/test_*.rb'
end

task :default => :test

Now, the tests run by simply calling rake or rake test. That should give us the following output:

Run options: --seed 41508

# Running:

..

Finished in 0.001047s, 1910.2197 runs/s, 1910.2197 assertions/s.

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

As you can see, our tests passed. Let’s add some more…

# file: lib/test_calculator_basic.rb
require 'minitest/autorun'
require 'calculator'

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

  # add
  def test_add
    assert_equal 4, @calc.add(2, 2)
  end

  def test_failing_add
     refute_equal 5, @calc.add(2, 2)
  end

  # subtract
  def test_subtract
    assert_equal 0, @calc.subtract(2, 2)
  end

  def test_failing_subtract
    refute_equal -1, @calc.subtract(2, 2)
  end
end

The results of this are similar to the last ones.

Run options: --seed 4571

# Running:

....

Finished in 0.001081s, 3700.2775 runs/s, 3700.2775 assertions/s.

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

You may have noticed that the test methods are starting to get more confusing to read. This is because you’re limited by_underscored_names_as_tests. This is where shoulda-context comes in. The good folks over at thoughtbot created this library to help give context to unit tests. Here is the above test written in shoulda-context format:

# file: lib/test_calculator.rb
require 'minitest/autorun'
require 'shoulda/context'
require 'calculator'

class TestCalculator < Minitest::Test
  context 'a calculator' do
    setup do # notice the difference
      @calc = Calculator.new
    end

    should 'add two numbers properly' do
      assert_equal 4, @calc.add(2, 2)
    end

    should 'not add incorrectly' do
      refute_equal 5, @calc.add(2, 2)
    end

    should 'subtract two numbers properly' do
      assert_equal 0, @calc.subtract(2, 2)
    end

    should 'not subtract incorrectly' do
      refute_equal -1, @calc.subtract(2, 2)
    end
  end
end

If you haven’t yet, run gem install shoulda-context in your terminal to install shoulda-context.

As you can see, the tests are much more readable. A human can read the tests like this: context: should 'do something'. For example, the first test can be read as: a calculator should add two numbers properly. This is much more expressive than the previous example. The tests now seem more Ruby-esque.

Expanding: Output

If you’ve been following along with the code examples, then you may notice that the test output isn’t as nice as RSpec or Cucumber’s output. To fix this, we can use a little gem called minitest-reporters.

First, we’re going to split up our tests a little more. If you have experience with Rails and testing, then you have probably used test_helper.rb. We can do the same thing in our project by just creating a file in our tests directory called test_helper.rb

Inside of test_helper.rb, we’re going to put the first two beginning require statements that we have been using in our tests.

# file: test/test_helper.rb
require 'minitest/autorun'
require 'shoulda/context'

Notice that we did not include require 'calculator'. This is done because we could expand our library at some point beyond just the Calculator class. Since not everything that we test in our library will need to include calculator, we do not include it in test_helper.rb.

Our calculator tests file should now look something like this:

# file: test/test_calculator.rb
require 'test_helper'
require 'calculator'

# ... lots of tests ...

To setup minitest-reporters with our new setup, first install the gem (gem install minitest-reporters). Now, open up test_helper.rb again and add the following:

# file: test/test_helper.rb
require 'minitest/autorun'
require 'minitest/reporters' # requires the gem
require 'shoulda/context'

Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new # spec-like progress

If we run the tests (ruby -I test:lib test/test_calculator.rb or rake test with only one testing file), we should see:

Started

TestCalculator
  test: a calculator should subtract two numbers properly.       PASS (0.00s)
  test: a calculator should not add incorrectly.                 PASS (0.00s)
  test: a calculator should add two numbers properly.            PASS (0.00s)
  test: a calculator should not subtract incorrectly.            PASS (0.00s)

Finished in 0.00141s
4 tests, 4 assertions, 0 failures, 0 errors, 0 skips

Much more readable and it has color! Notice how the format is the same as how the tests read to a human. This is how it should be.

Conclusion

Testing is not only recommended, but it is almost required when working on a Ruby project. Tools like shoulda-context are here to help ease that process. We covered Minitest basics, automated testing, shoulda-context, and fancy (colored, human-readable) output. If you’d like to learn more, check out these links:

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://benlakey.com/ Ben Lakey

    Hi Jesse. You have a typo in the link to the minitest repo (seatle instead of seattle). Thanks for the blog post.

  • Matt Campbell

    Great post! I just am now wondering: why would one use `shoulda/context` instead of just `MiniTest::Spec` (which you get for free when you `require “minitest/autorun”`) and which provides RSpec-like matchers and contexts out of the box?

    Also, thanks for pointing out `minitest-reporters` – looks cool!