こんにちは。@koshi_life です。
Rails minitest 使い方(具体例あり) - Mock編
に続き、実務でRailsでAPIを1本作る機会があったので、 Controller の minitest の書き方例を備忘します。
前提
- Ruby 2.6.0
- Rails 5.2.2
- DB: MongoDB v4.0.4
テスト対象モジュール
ユーザがメッセージを登録できる POST /message
で受け付けるシンプルなAPIをテスト対象とします。
リクエストヘッダー x-user-token
が Userテーブルに登録済みの access_token
に一致しない場合はエラーにするようなAPIをテスト対象モジュールとします。
準備
アプリ作成、モジュール追加、モデル作成,コントローラー作成コマンド。
$ rails new hello-rails-api --skip-active-record --api
$ cd hello-rails-api
$ echo "gem 'mongoid'" >> Gemfile
$ bundle install --path vendor/bundle
$ rails g mongoid:config
$ rails g model user email password access_token
$ rails g model message title body
$ rails g controller message
ソース
APIはのロジックは以下の通り。
app/controllers/message_controller.rb
class MessageController < ApplicationController
# POST /messages
def create
title = params['title']
body = params['body']
email = params['email']
token = request.headers['x-user-token']
# 必須パラメータが存在しない場合は 400 返却
if title.blank? or body.blank? or email.blank? or token.blank?
render :status=> 400, :json => {result: 'ng', code: 'ng-001'} and return
end
# email が存在しない場合は 404 返却
begin
user = User.find_by(email: email)
rescue
render :status=> 404, :json => {result: 'ng', code: 'ng-002'} and return
end
# token が一致しない場合は 403 返却
unless user.access_token == token
render :status=> 403, :json => {result: 'ng', code: 'ng-003'} and return
end
if Message.create(title: title, body: body, user_id: user.id)
# 保存が成功したら 201 返却
render :status=> 201, :json => {result: 'ok', code: 'ok-001'} and return
else
# 保存が失敗したら 500 返却
render :status=> 500, :json => {result: 'ng', code: 'ng-004'} and return
end
end
end
config/routes.rb
Rails.application.routes.draw do
post 'message', to: 'message#create'
end
app/models/user.rb
class User
include Mongoid::Document
field :email, type: String
field :password, type: String
field :access_token, type: String
has_many :messages
end
app/models/message.rb
class Message
include Mongoid::Document
field :title, type: String
field :body, type: String
belongs_to :user
end
テストコード
DB登録失敗以外の分岐を通るようにテストケースを書いてみました。
test/controllers/message_controller_test.rb
require 'test_helper'
class MessageControllerTest < ActionDispatch::IntegrationTest
USERS = [
User.new(email:'taro@example.com', password: 'pass1234', access_token:'taro-token'),
]
User.collection.drop
Message.collection.drop
USERS.each { |user| user.save }
URL = '/message'
test "必須パラメーター不足 400 返却" do
# headers: x-user-token がない
post URL, params: { title: 'title', body: 'body-1', email: 'email-1'}, as: :json
assert_response 400
res = JSON.parse(response.body)
assert_equal('ng', res['result'])
assert_equal('ng-001', res['code'])
# params.title がない
post URL, headers: { 'x-user-token': 'token' }, params: { body: 'body-1', email: 'email-1'}, as: :json
assert_response 400
res = JSON.parse(response.body)
assert_equal('ng', res['result'])
assert_equal('ng-001', res['code'])
# params.body がない
post URL, headers: { 'x-user-token': 'token' }, params: { title: 'title', email: 'email-1'}, as: :json
assert_response 400
res = JSON.parse(response.body)
assert_equal('ng', res['result'])
assert_equal('ng-001', res['code'])
# params.email がない
post URL, headers: { 'x-user-token': 'token' }, params: { title: 'title', body: 'body-1', }, as: :json
assert_response 400
res = JSON.parse(response.body)
assert_equal('ng', res['result'])
assert_equal('ng-001', res['code'])
# headers, params 全部ない
post URL
assert_response 400
res = JSON.parse(response.body)
assert_equal('ng', res['result'])
assert_equal('ng-001', res['code'])
end
test "emailが存在しない 404 返却" do
post URL, headers: { 'x-user-token': 'token' }, params: { title: 'title', body: 'body-1', email: 'ichiro@exampl.com'}, as: :json
assert_response 404
res = JSON.parse(response.body)
assert_equal('ng', res['result'])
assert_equal('ng-002', res['code'])
end
test "tokenが一致しない 403 返却" do
post URL, headers: { 'x-user-token': 'xxx' }, params: { title: 'title', body: 'body-1', email: 'taro@example.com'}, as: :json
assert_response 403
res = JSON.parse(response.body)
assert_equal('ng', res['result'])
assert_equal('ng-003', res['code'])
end
test "登録成功 201 返却" do
post URL, headers: { 'x-user-token': 'taro-token' }, params: { title: 'title', body: 'body-1', email: 'taro@example.com'}, as: :json
assert_response 201
res = JSON.parse(response.body)
assert_equal('ok', res['result'])
assert_equal('ok-001', res['code'])
end
end
実行例
$ rails test test/controllers/message_controller_test.rb
Running via Spring preloader in process 53418
Run options: --seed 59034
# Running:
....
Finished in 0.222477s, 17.9794 runs/s, 107.8763 assertions/s.
4 runs, 24 assertions, 0 failures, 0 errors, 0 skips
簡単ですが、以上です。