Keeping minitest Dry
Here’s a test written with minitest as extended for Rails. It is from the open source Rails project, IACCDB.
require 'test_helper'
module Admin
class MemberControllerTest < ActionController::TestCase
setup do
@member_list = create_list(:member, 12)
@before_attrs = @member_list.first.attributes
@after_attrs = @before_attrs.merge(
{'family_name' => Faker::Name.last_name}
)
end
test 'non-admin cannot view index' do
get :index
assert_response :unauthorized
end
test 'non-admin cannot show member' do
get :show, params: { id: @member_list.first.id }
assert_response :unauthorized
end
test 'non-admin cannot patch update' do
patch :update, params: { id: @after_attrs['id'], member: @after_attrs }
assert_response :unauthorized
end
test 'admin can get index' do
http_auth_login(:admin)
get :index
assert_response :success
end
test 'admin can get show' do
http_auth_login(:admin)
get :show, params: { id: @member_list.first.id }
assert_response :success
end
test 'admin can patch update' do
http_auth_login(:admin)
patch :update, params: { id: @after_attrs['id'], member: @after_attrs }
assert_response :redirect
member = Member.find(@after_attrs['id'])
assert_not_nil(member)
assert_equal(@after_attrs['family_name'], member.family_name)
end
end
end
The test validates that an unauthenticated user cannot access some member administration controller methods. It validates that an authenticated user can access the methods.
A few things about this test file are troublesome:
- Primarily, I don’t like the repetiton of
http_auth_login(:admin)
at the front of each of the authenticated tests. - There is some repetition in the test naming: “non-admin cannot”, “admin can”
- There are two groups of tests here that aren’t in any way grouped. Each group calls the same endpoints with different setup and expected results.
thoughtbot/shoulda-context
One solution is to use the thoughtbot/shoulda-context gem. The shoulda-context gem adds some DSL to minitest for defining contexts of tests. Now the non-admin tests and the admin tests each have their own context group. The admin context group has additional setup that arranges the http basic authentication.
Here is a link to a
commit with the diff
that shows all of the changes. It adds the gem and includes
the DSL additions
in the test_helper.rb
file.
Here is the new test file. The test implementations themselves did not
change, and are omitted here. The structure of the test file changed with
context
and should
DSL methods.
You can find an additional setup
block within the
allow admin
context that takes care of authenticating the
admin user for that context.
require 'test_helper'
module Admin
class MemberControllerTest < ActionController::TestCase
setup do
@member_list = create_list(:member, 12)
@before_attrs = @member_list.first.attributes
@after_attrs = @before_attrs.merge(
{'family_name' => Faker::Name.last_name}
)
end
context 'deny non-admin' do
should 'get index' do
# ...
end
should 'get show' do
# ...
end
should 'patch update' do
# ...
end
end
context 'allow admin' do
setup do
http_auth_login(:admin)
end
should 'get index' do
# ...
end
should 'get show' do
# ...
end
should 'patch update' do
# ...
end
end
end
end
This is more satisfying because it:
- Calls out that we’re testing the same endpoints with two setups
- Avoids repeating the
http_auth_login
call on each of the authorized tests - Avoids repeating
deny non-admin
andallow admin
in the test names
Footnote
Rails adds some DSL shortcuts through ActiveSupport::TestCase for defining test
methods with test
; defining setup and teardown methods with
setup
and teardown
.