AWS Developer Tools Blog
From RSpec to Minitest
One of my favorite aspects of working with Ruby is how natural it is to write tests for. The Ruby community does an excellent job of encouraging authors to produce well tested code. There is a plethora of well supported tools to choose from. I like to joke that new Ruby developers write Micro test frameworks instead of “Hello World!”.
Much of the Ruby code I have maintained uses RSpec. Lately I have been spending some time with Minitest. You may have worked with Minitest — it ships as part of the Ruby standard library.
Why bother learning another testing framework when your current tool suits your needs? My answer is why not? It’s always good to expand your horizons. I have found learning a new testing tool expands my ability to write good tests. I pick up new patterns and the context switch forces to me to question my standard testing approaches. As a result, I tend to write better tests.
Minitest::Spec
Minitest::Spec does a great job of bridging the gap between RSpec-style specifications and Minitest-style unit tests.
Here is an example RSpec test file:
require 'spec_helper' describe MyClass do describe '#some_method' do it 'returns a a string' do MyClass.new.should be_a(String) end end end
And the same thing using Minitest::Spec:
require 'test_helper' describe MyClass do describe '#some_method' do it 'returns a string' do MyClass.new.some_method.must_be_kind_of(String) end end end
Matchers
The primary difference above is how you make assertions. RSpec-style should
matchers can be converted to Minitest expectations with ease. The table below gives a few examples.
RSpec Matcher | Minitest Matcher |
---|---|
obj.should be(value) | obj.must_be(value) |
obj.should be_empty | obj.must_be_empty |
obj.should be(nil) | obj.must_be_nil |
obj.should equal(value) | obj.must_equal(value) |
lambda { … }.should raise_error(ErrorClass) | lambda { … }.must_raise(ErrorClass) |
See the Minitest api documentation for more expectations.
Mocks
Mocks (and stubs) are where the two testing libraries differ the most. RSpec provides doubles; Minitest provides a Mock class.
@mock = Minitest::Mock.new
You can set expectations about what messages the mock should receive. You name the method to expect, what to return from the mock and then optionally the arguments the method should receive. Given I need a mock for a user, and I expect the user’s delete method will be called, I could do the following:
user = Minitest::Mock.new user.expect(:delete, true) # returns true, expects no args UserDestoyer.new.delete_user(user) assert user.verify
Calling #verify
is necessary for the mock to enforce the expectations. RSpec makes this a little easier, but its not a huge adjustment.
Stubs
Stubs are pretty straight forward. Unlike RSpec, the stub lasts only till the end of the block. You also cannot stub methods that don’t exist yet.
Time.stub :now, Time.at(0) do assert obj_under_test.stale? end
Summary
Minitest works well and I’m impressed by how fast it run tests. Coming from RSpec you may find yourself missing features. Instead of trying to find exact replacements, consider using plain old Ruby solutions instead. You may find you write better tests. I still enjoy working with RSpec and appreciate it for its strengths, but you should also consider giving Minitest a spin.