Make Rails Use MiniTest::Spec!

Rails 4 was on its way to using MiniTest::Spec as the superclass for ActiveSupport::TestCase. But in one of many reversals, the work was pulled by this commit. DHH says that Rails is omakase and I am cool with that. But since Ruby is wide open for us to change it and we are free in DHH's words to "freedom patch" what we see fit, we can fix Rails.

The minitest-spec-rails gem makes it easy to use the MiniTest::Spec DSL within your existing Rails 3 or 4 test suite. It does this by forcing ActiveSupport::TestCase to subclass MiniTest::Spec.

Build Status

Usage

Existing or new Rails 3 or 4 applications that use the default Rails testing structure can simply drop in the minitest-spec-gem and start writing their tests in the new spec DSL. Since MinitTest::Spec is built on top of MiniTest::Unit, a replacement for Test::Unit, all of your existing tests will continue to work.

This gem uses no generators and does not require you to write your tests in a new directory or file structure. It is completely different than minitest-rails which is setup more like RSpec in the way it bolts on the side of Rails. The goal of this project is to make Rails 3 or 4 applications just work as if rails-core had decided to support MiniTest::Spec. Eventually that day will come and if it does, all your tests will still work! So bundle up and get started!

gem 'minitest-spec-rails'

Test Styles

This cheat sheet shows both the MiniTest::Unit assertions along with the MiniTest::Spec assertion syntax. Remember, MiniTest::Spec is build on top of MiniTest::Unit which is a Test::Unit replacement. That means you can mix and match styles as you upgrade from Test::Unit to a more modern style. For example, both of these would work in MiniTest::Spec and are interchangeable.

# MiniTest::Unit Assertion Style:
assert_equal 100, foo

# MiniTest::Spec Assertion Style:
foo.must_equal 100

All existing Rails test cases that subclass ActiveSupport::TestCase will continue to work and I personally suggest that you still use what I call the subclass convention vs the outer describe test case convention. However either will work.

require 'test_helper'
class UserTest < ActiveSupport::TestCase
  let(:user_ken)   { User.create! :email => '[email protected]' }
  it 'works' do
    user_ken.must_be_instance_of User
  end
end
require 'test_helper'
describe User do
  # This will work too.
end

Just for reference, here is a full list of each of Rails test case classes and the matching describe alternative if one exists. Remember, names are important when using the describe syntax. So you can not have a mailer named FooBar and expect it to work with the outer describe spec style since there is no way to map the spec type based on an existing naming convention.

# Model Test
class UserTest < ActiveSupport::TestCase
end
describe User do
end

# Controller Test
class UsersControllerTest < ActionController::TestCase
end
describe UsersController do
end

# Integration Tests - Must use subclass style!
class IntegrationTest < ActionDispatch::IntegrationTest
end

# Mailer Test
class UserMailerTest < ActionMailer::TestCase
end
describe UserMailer do
end

# View Helper Test
class UsersHelperTest < ActionView::TestCase
end
describe UsersHelper do
end

Gotchas

If you are upgrading from Test::Unit, there are a few missing assertions that have been renamed or are no longer available within MiniTest.

  • The method assert_raise is renamed assert_raises.
  • There is no method assert_nothing_raised. There are good reasons for this on Ryan's blog entry.

If you are using minitest-spec-rails with Rails 3.0, then your controller or mailer tests will need to use the tests interface for the test to be setup correct within sub describe blocks. I think this is a bug with class_attribute within Rails 3.0. Rails 3.1 and higher does not exhibit this problem.

Contributing

The minitest-spec-rails gem is fully tested from Rails 3.0 to 4 and upward. We run our tests on Travis CI in both Ruby 1.8 and 1.9. If you detect a problem, open up a github issue or fork the repo and help out. After you fork or clone the repository, the following commands will get you up and running on the test suite.

$ bundle install
$ bundle exec rake appraisal:setup
$ bundle exec rake appraisal test

We use the appraisal gem from Thoughtbot to help us generate the individual gemfiles for each Rails version and to run the tests locally against each generated Gemfile. The rake appraisal test command actually runs our test suite against all Rails versions in our Appraisal file. If you want to run the tests for a specific Rails version, use rake -T for a list. For example, the following command will run the tests for Rails 3.2 only.

$ bundle exec rake appraisal:rails32 test

Our current build status is:

Build Status