Use Minitest for Your Next Rails Project
Minitest is a fast, easy to read alternative to RSpec for writing Rails tests, but it can be confusing at first. Here’s how I set up Minitest with Rails, and the gotchas I encountered along the way.
Minitest seems to be enjoying a spike in popularity; my interest was piqued by Brandon Hilkert’s article, 7 Reasons I’m Sticking with Minitest and Fixtures in Rails. But when it came time to actually setting up my Rails project, I found Minitest+Rails documentation to be outdated or lacking.
tl;dr – Jump straight to the code recommendations.

Minitest is confusing!
If you decide to jump on the Minitest bandwagon, you’ll probably run into these gotchas too. Here is what I discovered.
TestUnit and Minitest are the same thing… except not
Test::Unit
was the name of the default Rails testing framework up through Rails 3. Starting with Rails 4, the core functionality and assertions were replaced by Minitest, but confusingly, in some places the name “test unit” remains.1 Just be assured that if you are using Rails 4 or higher, then “test unit” simply means Minitest, with some Rails-specific syntactic sugar sprinkled on top.
If you search the web and come across blog posts, Stack Overflow answers, or gems that refer to Test::Unit
, they are probably talking about Rails 3 and below. Pay close attention to the publication date and the Rails version referenced to make sure those gems and recommendations are still applicable.
Minitest has two different syntaxes
Minitest is a single testing framework, but it ships with two completely different DSLs: the “assert” (or “xUnit”) style DSL and the BDD (behavior-driven development) style. I am only concerned with the assertion style, since that is what is promoted by Rails itself, and is what differentiates Minitest from RSpec. My reasoning: if I wanted to write BDD style, I’d just stick with RSpec!
Minitest’s default output is a step backwards
Minitest makes a bad first impression, because its default output on the console is pretty bland: no color! This is something I took for granted as an RSpec user. Surprisingly, the tutorials I found for Minitest did not address this.2 Luckily I discovered the minitest-reporters gem, which does everything I need (including a nifty progress bar).
The directory structure is not immediately obvious
Minitest itself does not care how you organize or name your tests, but the Rails ecosystem uses a certain set of conventions. Unfortunately these conventions have changed recently, so I found myself getting conflicting suggestions from various places online. Rails previously structured its tests based on the type of test (unit vs functional), but now the structure is basically a mirror of the app/
directory (controllers, models, helpers). I’ve documented the correct project layout below.
The “Rails way” to write tests differs from Minitest tutorials
Rails documentation and tutorials declare tests as:
test "my feature" do
end
Whereas the Minitest way is:
def test_my_feature
end
Likewise, the Rails convention is to name test classes as e.g. UserTest
, versus Minitest’s TestUser
. Both styles will work fine, but for consistency with the Rails generators, I find it’s best to stick with the Rails conventions. Just be aware that the test "my feature" do ... end
style is Rails syntactic sugar and is not available if you are using Minitest outside of Rails.3
Minitest’s mocking library is not great
Minitest comes with its own Minitest::Mock
library, but it is very basic. If you are going to do any serious mocking or stubbing in your tests, you will probably need to go elsewhere. My recommendation is the mocha gem, which works great with Rails and Minitest.
Minitest or MiniTest?
If all that wasn’t confusing enough, there is also the spelling issue: Minitest was also spelled as MiniTest
earlier in its lifetime. The correct capitalization in the latest version is Minitest
(lowercase “t”).
Set up your project
Still interested in using Minitest? Here’s a quick rundown on how to set up your next Rails project.
Gemfile
No additions to the Gemfile are strictly required, since Rails ships with the bare minimum you need to write tests using Minitest. However I find guard invaluable for my TDD workflow, and capybara, poltergeist, mocha and shoulda-matchers come in handy for most Rails projects.
group :development do
gem "guard", ">= 2.2.2", :require => false
gem "guard-minitest", :require => false
gem "rb-fsevent", :require => false
gem "terminal-notifier-guard", :require => false
end
group :test do
gem "capybara"
gem "connection_pool"
gem "launchy"
gem "minitest-reporters"
gem "mocha"
gem "poltergeist"
gem "shoulda-context"
gem "shoulda-matchers", ">= 3.0.1"
gem "test_after_commit"
end
test/test_helper.rb
The test_helper.rb
file is responsible for loading Rails and configuring the test framework. In my projects I’ve added best practices for using Minitest with Capybara and Poltergeist.
ENV["RAILS_ENV"] ||= "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "mocha/mini_test"
# Improved Minitest output (color and progress bar)
require "minitest/reporters"
Minitest::Reporters.use!(
Minitest::Reporters::ProgressReporter.new,
ENV,
Minitest.backtrace_filter)
# Capybara and poltergeist integration
require "capybara/rails"
require "capybara/poltergeist"
Capybara.javascript_driver = :poltergeist
class ActiveSupport::TestCase
fixtures :all
end
class ActionDispatch::IntegrationTest
include Capybara::DSL
end
# See: https://gist.github.com/mperham/3049152
class ActiveRecord::Base
mattr_accessor :shared_connection
@@shared_connection = nil
def self.connection
@@shared_connection || ConnectionPool::Wrapper.new(:size => 1) { retrieve_connection }
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
Project layout
Use this folder structure for organizing your tests. The controllers, helpers, mailers, and models folders are used for testing their /app
counterparts. The odd one out is lib
, which goes in test/unit/lib
.
test/
test_helper.rb
controllers/
fixtures/
helpers/
integration/ <- Capybara tests go here
mailers/
models/
unit/lib/ <- Tests for your /lib code
Guardfile
Run guard
and it will watch your filesystem and automatically run the corresponding tests whenever you save a file. This is super handy for a quick TDD workflow. Here’s the Guardfile configuration that makes this work:
guard :minitest, :spring => true do
watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
watch(%r{^app/controllers/application_controller\.rb$}) { "test/controllers" }
watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" }
watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
watch(%r{^app/workers/(.+)\.rb$}) { |m| "test/unit/workers/#{m[1]}_test.rb" }
watch(%r{^lib/(.+)\.rb$}) { |m| "test/unit/lib/#{m[1]}_test.rb" }
watch(%r{^lib/tasks/(.+)\.rake$}) { |m| "test/unit/lib/tasks/#{m[1]}_test.rb" }
watch(%r{^test/.+_test\.rb$})
watch(%r{^test/test_helper\.rb$}) { "test" }
end
Conclusion
Making the switch from RSpec to Minitest was a bit harder than I expected, but that was mostly due to outdated or scattered documentation. The framework itself is simple and a breeze to use.
For the most part, testing tools that I enjoyed from the RSpec ecosystem, like guard, capybara, simplecov, and vcr also work great with Minitest, so I don’t feel like I’m missing anything. I’m also finding that I prefer fixtures over factory_girl, although that’s perhaps the subject of another article.
By the way, if you’d like to use the project setup I’ve outlined in this article, check out my rails-template project. It makes setting up a new Rails app with all these Minitest goodies as simple as:
rails new blog \
-d postgresql \
-m https://raw.githubusercontent.com/mattbrictson/rails-template/master/template.rb
Happy testing!
-
The railtie for loading the Minitest-based testing framework is still named
rails/test_unit/railtie
. Likewise, to disable Minitest in Rails, you pass the--skip-test-unit
flag when runningrails new
. ↩ -
Some articles mention
minitest/pride
, which makes the output rainbow-colored. Entertaining, but not very practical. ↩ -
More specifically, these bits of extra syntax are provided by ActiveSupport. For example, the
test
DSL is defined in ActiveSupport::Testing::Declarative. ↩
You just read
Use Minitest for Your Next Rails Project
Minitest is a fast, easy to read alternative to RSpec for writing Rails tests, but it can be confusing at first. Here’s how I set up Minitest with Rails, and the gotchas I encountered along the way.
Share this post? Copy link
About the author
Hi! I’m a Ruby and CSS enthusiast, regular open source contributor, software engineer, and occasional blogger writing from the San Francisco Bay Area. Thanks for stopping by! —Matt