Make Your Ruby Tests Cleaner with Minitest and ShouldaBy Jesse Herrick
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
- Ruby (duh; current 2.1.2 would be best)
- The ability to run
gem install GEMwithout 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:
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
# 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 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.
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, 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
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.
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:
- Minitest Library (included with Ruby)
- Minitest Repo (official repository)
- shoulda-context (shoulda-context GitHub page)
- minitest-reporters (minitest-reporters GitHub page)
- Code from This Article (all of the code from this article)
- Extreme Programming – Wikipedia (XP wikipedia page)
- Extreme Programming – Official (XP official website)
- DDH Offended by RSpec (old, but still relevant)