MiniTest – Ruby 1.9’s test framework
(this is a classic post I wrote two years ago for another site which is shutting down, saving here since it’s still very useful)
Aaron Patterson gave a talk at GoGaRuCo 2010 about the latest changes in Ruby 1.9.2’s standard library and one of the topics he spoke on was MiniTest. The Ruby community has been particularly innovative in the world of testing and Ruby 1.8’s Test::Unit library is circa 2003, providing nothing but the most basic testing API. Every Ruby project I’ve ever worked on has skipped Test::Unit and pulled in many different test gems (e.g. rspec, shoulda, mocha, flexmock) to provide a modern test infrastructure.
With Test::Unit, you just subclass Test::Unit::TestCase, name your methods starting with ‘test’ and include one or more assertions to verify:
class TestSomething < Test::Unit::TestCase def test_foo foo = Foo.new assert foo bar = nil assert_nil bar end end
Believe it or not, Test::Unit is actually rather slow and bloated. It includes a number of GUIs (GTk v1, GTk v2, FxRuby) that are rarely if ever used. A revamp was needed…
Ruby 1.9 includes an updated version of the venerable Test::Unit which removes a lot of the more esoteric features, called MiniTest. You don’t need to do anything to use MiniTest in 1.9, it replaces Test::Unit and provides a backwards compatible API that provides the 90% of Test::Unit that people were using often. You can even use minitest on Ruby 1.8 by installing the `minitest` gem. Aside from the Test::Unit API, several improvements over Test::Unit are included:
When you run your test suite, you might notice this at the bottom:
Test run options: --seed 1261
This is because MiniTest by default runs your tests in random order. This is a good thing because it prevents your tests from accidentally becoming order-dependent due to “state leakage”. If you find that your tests are breaking randomly, it is most likely due to this state leakage. You can run your tests with the same seed to reproduce the problem:
MiniTest allows you to easily skip tests that are not working with the `skip` method:
def test_foo skip("Need to debug this...") assert_equal false, true end
which results in this:
83 tests, 106 assertions, 0 failures, 0 errors, 1 skips
MiniTest also has a ‘-v’ flag which will print out the time each test takes - excellent for determining those tests which are slowing down your test suite:
which emits a line for each test. It’s too bad it doesn’t have an option for sorting or a minimum time (like 0.1 sec); this would be useful for suites which have thousands of tests.
TestMemCache#test_check_size_off: 0.02 s: . TestMemCache#test_check_size_on: 0.01 s: . TestMemCache#test_consistent_hashing: 0.11 s: . TestMemCache#test_crazy_multithreaded_access: 0.00 s: S TestMemCache#test_custom_encoding: 0.00 s: . TestMemCache#test_decr: 0.00 s: .
MiniTest also includes a basic BDD DSL like RSpec:
require 'minitest/spec' describe Meme do before do @meme = Meme.new end describe "when asked about cheeseburgers" do it "should respond positively" do @meme.i_can_has_cheezburger?.must_equal "OHAI!" end end describe "when asked about blending possibilities" do it "won't say no" do @meme.does_it_blend?.wont_match /^no/i end end end
It even includes a basic mocking API which you can read about in the MiniTest documentation. These changes are a great step forward for Ruby’s standard test library. Personally I’m going to use MiniTest on my next project and see if I can slim down my test dependencies. Happy coding!