←Rails 6で認証認可入り掲示板APIを構築する #4 postのバリデーション、テスト実装
controllerを作る
前回はmodelを作ったので、今回はcontrollerを実装していきます。
$ rails g controller v1/posts
実行するとcontrollerとrequest specファイルが生成されます。
とりあえずcontrollerを以下まで実装します。
# frozen_string_literal: true
module V1
#
# post controller
#
class PostsController < ApplicationController
before_action :set_post, only: %i[show update destroy]
def index
# TODO
end
def show
# TODO
end
def create
# TODO
end
def update
# TODO
end
def destroy
# TODO
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.permit(:subject, :body)
end
end
end
- index: post一覧を取得する
- show: post1レコードの情報を取得する(R)
- create: post1レコードを作成する(C)
- update:post1レコードを更新する(U)
- destroy: post1レコードを削除する(D)
CRUDに沿ったcontrollerを、一旦ロジック無しで作ります。
なお、V1というnamespaceを切っているのはAPI開発ではよくやる手法です。
これにより後方互換の無いversion2を作る際、分離して開発がしやすくなります。
続いてroutesを設定します。
# frozen_string_literal: true
Rails.application.routes.draw do
+ namespace "v1" do
+ resources :posts
+ end
end
これでCRUDのroutesが設定されます。確認してみましょう。
$ rails routes
...
Prefix Verb URI Pattern Controller#Action
v1_posts GET /v1/posts(.:format) v1/posts#index
POST /v1/posts(.:format) v1/posts#create
v1_post GET /v1/posts/:id(.:format) v1/posts#show
PATCH /v1/posts/:id(.:format) v1/posts#update
PUT /v1/posts/:id(.:format) v1/posts#update
DELETE /v1/posts/:id(.:format) v1/posts#destroy
...
indexテストの実装
例によってテストを先に実装します。
挙動としては
- 登録されたpostを返す
- created_at降順でソート
- 20件でlimit
でいきます。
簡易的なテストアプリケーションのチュートリアルのためpagerは組み込みませんが、もしかしたら今後記事を書くかもしれません。
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "V1::Posts", type: :request do
describe "GET /v1/posts#index" do
before do
create_list(:post, 3)
end
it "正常レスポンスコードが返ってくる" do
get v1_posts_url
expect(response.status).to eq 200
end
it "件数が正しく返ってくる" do
get v1_posts_url
json = JSON.parse(response.body)
expect(json["posts"].length).to eq(3)
end
it "id降順にレスポンスが返ってくる" do
get v1_posts_url
json = JSON.parse(response.body)
first_id = json["posts"][0]["id"]
expect(json["posts"][1]["id"]).to eq(first_id - 1)
expect(json["posts"][2]["id"]).to eq(first_id - 2)
end
end
end
- beforeは、同ブロック以下itで毎回事前に実行されます。
-
create_list(:post, 3)
はpostを3レコード生成しDBに保存する処理です。 - また、itブロック終了時にテストDBはrollbackされます。
つまり挙動をまとめると、
LINE 10:it "正常レスポンスコードが返ってくる"ブロック開始
LINE 8:3件分のpostが保存される
LINE 11:v1_posts_url(v1/posts/index)にgetリクエストを行う
LINE 12:レスポンスコードが:ok(200 正常)
LINE 13:it "正常レスポンスコードが返ってくる"ブロック終了。rollbackされpostレコードは0件に
LINE 14:it "件数が正しく返ってくる"ブロック開始
LINE 8:3件分のpostが保存される
LINE 15:v1_posts_url(v1/posts/index)にgetリクエストを行う
LINE 16:response.bodyをJSON.parseしてRubyの配列に変換
LINE 17:レスポンスのpostが3レコード
LINE 18:it "件数が正しく返ってくる"ブロック終了。rollbackされpostレコードは0件に
↓
...
となります。
この時点ではcontroller未実装なので、当然テストはコケます。
なお、最後のテストは厳密にはcreated_atの比較が必要ですが、簡易的にidで比較をしています。
本来はlimitのテストもすべきですが省略します。興味があれば、create_listで21件作って20件しか返ってこないことを確認するテストを実装してみてください。
Tips.
ついでによく使うfactoryBotのメソッドを紹介しておきます。
-
build(:post)
postを1レコード、メモリ上に生成。saveしない限りDBには反映されない。Post.newに相当 -
create(:post)
postを1レコード生成しDBに保存。Post.create!に相当 -
build_list(:post, 5)
postを5レコード生成。buildの複数版 -
create_list(:post, 5)
postを5レコード生成。createの複数版
example.comをhostsに追加
なお、requestsテストは以下対応が必要です。
...
config.hosts << ".amazonaws.com"
+ config.hosts << "www.example.com"
...
なぜならrspecのテストはwww.example.comからのリクエストとして認識されるためです。
indexの実装
...
def index
- # TODO
+ posts = Post.order(created_at: :desc).limit(20)
+ render json: { posts: posts }
end
...
これで一覧取得ができます。
試しにcurlでAPIを叩いてみます。
$ curl localhost:8080/v1/posts
{"posts":[{"id":2,"subject":"","body":"hoge","created_at":"2020-09-06T01:07:52.628Z","updated_at":"2020-09-06T01:07:52.628Z"},{"id":1,"subject":"hoge","body":"fuga","created_at":"2020-09-05T13:50:01.797Z","updated_at":"2020-09-05T13:50:01.797Z"}]}
もし空のdataが返ってきた場合は、rails c
からpostのレコードを生成してみてください。
ここまでできたら、rubocopやrspec実行を忘れずに行った後、git commitしましょう。
続き
→Rails 6で認証認可入り掲示板APIを構築する #6 show, create実装
【連載目次へ】