Make Your Ruby Tests Cleaner with Minitest and Shoulda

Share this article

Key Takeaways

  • Minitest has replaced Test Unit as the included testing framework for Ruby since Ruby 1.9.3. Minitest is a lightweight, fast, and clean testing framework that supports TDD, BDD, mocking, and benchmarking. It’s simpler and has less syntax than other frameworks like RSpec, making it easier to read and write tests.
  • Shoulda-context is a library created by thoughtbot that can be used with Minitest to make unit tests more expressive and readable. It allows you to define a context and specify what should happen in that context, making the tests read more naturally to a human.
  • Minitest-reporters is a gem that can be used to improve the output of Minitest tests. It provides a more readable, colored, human-readable output that matches the way the tests are written.
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:

Frequently Asked Questions (FAQs) about Minitest and Shoulda

What are the key differences between Minitest and other testing frameworks?

Minitest is a lightweight, fast, and clean testing framework for Ruby. It provides a complete suite of testing facilities supporting TDD, BDD, mocking, and benchmarking. Unlike other testing frameworks like RSpec, Minitest is simpler and has less syntax, making it easier to read and write tests. It’s also part of the Ruby standard library, so you don’t need to install any additional gems to use it.

How do I get started with Minitest?

To get started with Minitest, you need to require ‘minitest/autorun’ at the top of your test file. Then, create a class that inherits from Minitest::Test and define your test methods. Each test method should start with ‘test_’. To make an assertion, you can use the assert method, which takes a boolean and fails if the boolean is false.

How can I use Shoulda with Minitest?

Shoulda is a testing library that works with Minitest and provides additional matchers to make your tests more expressive and readable. To use Shoulda with Minitest, you need to add the shoulda-context and shoulda-matchers gems to your Gemfile and bundle install. Then, require ‘shoulda/context’ and ‘shoulda/matchers’ in your test file.

What are some common assertions in Minitest?

Minitest provides a variety of assertions to test your code. Some common ones include assert, which checks if a value is truthy, refute, which checks if a value is falsy, assert_equal, which checks if two values are equal, and assert_raises, which checks if a block of code raises a certain exception.

How can I run a specific test in Minitest?

To run a specific test in Minitest, you can use the -n or –name option followed by the test name when running your test file. For example, if you have a test named test_example, you can run it with ruby -Ilib:test your_test_file.rb -n test_example.

How can I use mocks and stubs in Minitest?

Minitest provides built-in support for mocks and stubs. A mock is an object that simulates the behavior of a real object in a controlled way, while a stub is a method that simulates the behavior of a real method. To create a mock, you can use Minitest::Mock.new. To create a stub, you can use the stub method, which takes a method name and a return value.

How can I write BDD-style tests with Minitest and Shoulda?

Shoulda allows you to write BDD-style tests with Minitest. Instead of defining test methods, you can define a context and specify what should happen in that context. For example, you can write context ‘when the user is logged in’ do; should ‘display the user’s name’ do; …; end; end.

How can I test performance with Minitest?

Minitest includes a benchmarking module that allows you to test the performance of your code. To use it, you need to require ‘minitest/benchmark’ and create a class that inherits from Minitest::Benchmark. Then, you can define your benchmark methods, which should start with ‘bench_’.

How can I customize the output of Minitest?

Minitest allows you to customize its output by creating a custom reporter. A reporter is an object that responds to certain methods like record, report, and start. To use a custom reporter, you need to add it to Minitest.reporter.reporters.

How can I integrate Minitest with Rails?

Minitest is the default testing framework in Rails. When you create a new Rails application, it automatically generates a test directory with a helper file that requires ‘minitest/rails’. You can write your tests in the test/models, test/controllers, and test/integration directories. To run your tests, you can use the rails test command.

Jesse HerrickJesse Herrick
View Author

Jesse Herrick is an avid Ruby developer who specializes in web development. He is a back-end developer at Littlelines and loves programming. You can read his personal blog at: https://jesse.codes.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week