Rails minitest 使い方(具体例あり) - API Controller 編

こんにちは。[@koshi_life](https://twitter.com/koshi_life) です。

[Rails minitest 使い方(具体例あり) - Mock編](https://qiita.com/koshilife/items/dcb1fc7ac0c4a676c17c)
に続き、実務でRailsでAPIを１本作る機会があったので、　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
```

簡単ですが、以上です。