←Rails 6で認証認可入り掲示板APIを構築する #5 controller, routes実装
showテストの実装
前回のindexに続き、showのテストとcontrollerを実装していきます。
showは、パラメータのidを元にfindして返すという挙動です。
テストは正常にレスポンスが返ってくることと、存在しないIDの時に404が返ってくることあたりでしょうか。
...
+ describe "GET /v1/posts#show" do
+ let(:post) do
+ create(:post, subject: "showテスト")
+ end
+ it "正常レスポンスコードが返ってくる" do
+ get v1_post_url({ id: post.id })
+ expect(response.status).to eq 200
+ end
+ it "subjectが正しく返ってくる" do
+ get v1_post_url({ id: post.id })
+ json = JSON.parse(response.body)
+ expect(json["post"]["subject"]).to eq("showテスト")
+ end
+ it "存在しないidの時に404レスポンスが返ってくる" do
+ get v1_post_url({ id: post.id + 1 })
+ expect(response.status).to eq 404
+ end
+ end
...
説明が漏れていましたが、letは呼び出された時に遅延評価されます。
つまりpostという変数が呼ばれるまでは実行されず、呼ばれた時点で実行されます。
上記例だと、itブロックの中でpost.idと呼ばれた時にcreateされます。
当然、controller未実装なのでテストはコケます。
showの実装
...
def show
- # TODO
+ render json: { post: @post }
end
...
これで追加した3テストのうち2つは通過しますが、残り1個404エラーのテストが通過しません。
RailsでRecordNotFound例外が起き、jsonじゃないレスポンスが返っちゃっていますね。
全てのcontrollerのsuperクラスであるapplication_controller.rbを修正します。
# frozen_string_literal: true
#
# controllerのsuperクラス
#
class ApplicationController < ActionController::API
+ rescue_from ActiveRecord::RecordNotFound, with: :render_404
+ def render_404
+ render status: 404, json: { message: "record not found." }
+ end
end
※無駄に2行空いているのはQiitaのシンタックスハイライトがうまく効かないだけなので、本来は1行でOKです
上記のように対応することで、レコードが見つからない(ActiveRecord::RecordNotFound)例外が投げられた時にrescueされ、render_404
が実行されます。
そしてstatus: 404
の通り404レスポンスコードでjsonが返されます。
ここまで実装するとテストも通過します。
念の為curlでも確認してみましょう。
$ curl localhost:8080/v1/posts/0
{"message":"record not found."}
$ curl localhost:8080/v1/posts/1
{"post":{"id":1,"subject":"hoge","body":"fuga","created_at":"2020-09-05T13:50:01.797Z","updated_at":"2020-09-05T13:50:01.797Z"}}
createテストの実装
createは新しいレコードを生成するので、1レコード増えること、増えたレコードが登録時と一致すること、不正なパラメータの時に生成できないことを確認します。
+ describe "POST /v1/posts#create" do
+ let(:new_post) do
+ attributes_for(:post, subject: "create_subjectテスト", body: "create_bodyテスト")
+ end
+ it "正常レスポンスコードが返ってくる" do
+ post v1_posts_url, params: new_post
+ expect(response.status).to eq 200
+ end
+ it "1件増えて返ってくる" do
+ expect do
+ post v1_posts_url, params: new_post
+ end.to change { Post.count }.by(1)
+ end
+ it "subject, bodyが正しく返ってくる" do
+ post v1_posts_url, params: new_post
+ json = JSON.parse(response.body)
+ expect(json["post"]["subject"]).to eq("create_subjectテスト")
+ expect(json["post"]["body"]).to eq("create_bodyテスト")
+ end
+ it "不正パラメータの時にerrorsが返ってくる" do
+ post v1_posts_url, params: {}
+ json = JSON.parse(response.body)
+ expect(json.key?("errors")).to be true
+ end
+ end
attributes_for
を使うとbuildと同じくDBにsaveしないでオブジェクトが返ってきます。
その際にActiveRecord形式ではなくシンプルなオブジェクトとして返ってくるかつ、idやcreated_at, updated_atというpost時に不要なカラムは存在しないので便利です。
なお、この際に変数名をpost
にしないようご注意ください。
筆者は記事執筆当時post
にしてしまい、post v1_posts_url, params: post
となりget, postのpostがオーバーライドされてテストが正常に動かない状態となりました…解消までかなり時間がかかりました
expect do post v1_posts_url, params: new_post end.to change { Post.count }.by(1)
このブロックはpostをすることでPost.countが1増加するテストとなります。expect.to eq
ではなくexpectがブロックになっているのがポイントです。
createの実装
controllerを実装します。
...
def create
- # TODO
+ post = Post.new(post_params)
+ if post.save
+ render json: { post: post }
+ else
+ render json: { errors: post.errors }
+ end
end
...
$ curl localhost:8080/v1/posts -X POST -H 'Content-Type: application/json' -d '{"subject":"moge","body":"hoge"}'
{"post":{"id":3,"subject":"moge","body":"hoge","created_at":"2020-09-06T10:31:38.375Z","updated_at":"2020-09-06T10:31:38.375Z"}}
rubocopとrspecが正常に動いたらcommit。
本記事においてとても参考にさせていただきました。ありがとうございます。
参考:Railsで超簡単API
続き
→Rails 6で認証認可入り掲示板APIを構築する #7 update, destroy実装
【連載目次へ】