Adding Minitest Spec in Rails 4
08 Jul 2013Rails 4 is out, and among its many improvements is upgrading the default testing library from Test::Unit to Minitest. And although Minitest has some surprisingly interesting features, the most discussed addition is its spec DSL. It is designed as a subset of RSpec’s DSL, though I’ll leave to others any direct comparisons to RSpec. Suffice it to say it its focus is to give you a friendly syntax to generate the test classes, methods, and assertions you’d normally write in plain Ruby.
It’ll take a little configuration, and yes, there’s a gem for that, but the DIY approach takes surprisingly little elbow grease and will teach you a couple of cool Minitest features. Let’s dive in!
Step 1: Setting the Minitest Dependency
Rails 4 sets the dependency on Minitest to “~> 4.2”. This means that it will use any Minitest 4.x release that is 4.2 or above. This also means that we can’t use the newly released Minitest 5, or the older 4.1. Since we want the spec DSL, we need to set the dependency to “~> 4.7”. To do that, let’s set the dependency in the Gemfile:
Step 2: Extending MiniTest::Spec::DSL
Minitest 4.7 introduced the MiniTest::Spec::DSL module. To add the spec DSL to our Rails tests, we’ll add this to the test/test_helper.rb
file.
Let’s require the source file just after the rails/test_help
require:
The second change is to extend MiniTest::Spec::DSL in ActiveSupport::TestClass. Luckily for us, there is already a place in the helper for us to make these changes:
Cool Minitest trick #1: register_spec_type
The last change is to tell MiniTest::Spec to use ActiveSupport::TestCase when describing an ActiveRecord model. We do this by calling Minitest’s register_spec_type method.
Step 3: Writing Specs
Now that we’ve configured the spec DSL, let’s use it! Let’s assume we have the following test in test/models/user_test.rb
:
We can convert this test to the spec DSL one section at a time. Let’s start with replacing the class with a describe
block:
We can bypass the need to explicitly define a class inheriting from ActiveSupport::TestCase
because User inherits from ActiveRecord and we registered the spec type in the previous step.
Next we can replace the test methods with it
blocks:
Now let’s replace the calls to the assertions with Minitest’s expectations. In the first test block, we are passing user.valid?
to assert
. The spec DSL provides many assertions as expectations, and in this case we can write this test using the must_be
expectation. That would look like this:
In the next test block, we are refuting that the user is valid. We can use the wont_be
expectation for that. And then we are asserting that there are errors on the email attribute. We can use a combination of the must_be
expectation and the present?
method Rails adds to clean that up a bit:
We can also move some helper methods to let
blocks. In the end, here is what the test can look like using the spec DSL:
Step 4: Smoothing The Rough Edges
The Minitest spec DSL does not support nested context
blocks, but it does support nested describe
blocks. Except… ActiveSupport::TestCase also defines a describe
method, which stomps on the spec DSL. Oh no!
But wait, this is Ruby! To use nested describe
blocks in your tests, we just need to remove the method from ActiveSupport::TestCase. To do this, add a call to remove_method
just before MiniTest::Spec::DSL is added in the test helper:
If you prefer expectations to assertions, we’ll need to add expectations for the several assertions that Rails provides, such as assert_response
, assert_redirected_to
, and assert_difference
. We can do all this in the test helper file.
Cool Minitest trick #2: infect_an_assertion
First, create a new module and use the method infect_an_assertion that Minitest provides:
Then we can include that module in Object so that those expectations are available everywhere:
Now we can use these expectations in our tests. Yay!
That’s a Wrap!
As you can see, Minitest and Rails go hand in hand. I would even go so far as to say they are BFFs, like I did in this presentation. I hope you give Minitest a shot. Don’t let its size fool you. It may be small, but it’s a surprisingly powerful, full-featured testing library.
If this seems like too much configuration to manage on your own, that’s okay! Feel free to check out the minitest-rails gem, which does all this for you, includes some handy rake tasks, and lets you generate tests using the spec DSL. Either way, hopefully you know a little more about the test framework that comes with Rails 4.