Rails 6で認証認可入り掲示板APIを構築する #6 show, create実装

←[Rails 6で認証認可入り掲示板APIを構築する #5 controller, routes実装](https://qiita.com/rf_p/items/2b40f08e0509fb8ff56b)

# showテストの実装

前回のindexに続き、showのテストとcontrollerを実装していきます。
showは、パラメータのidを元にfindして返すという挙動です。

テストは正常にレスポンスが返ってくることと、存在しないIDの時に404が返ってくることあたりでしょうか。

```diff:spec/requests/v1/posts_controller.rb
...

+  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の実装

```diff:app/controllers/v1/posts_controller.rb
...
     def show
-      # TODO
+      render json: { post: @post }
     end
...
```

これで追加した3テストのうち2つは通過しますが、残り1個404エラーのテストが通過しません。
RailsでRecordNotFound例外が起き、jsonじゃないレスポンスが返っちゃっていますね。

全てのcontrollerのsuperクラスであるapplication_controller.rbを修正します。

```diff:app/controller/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でも確認してみましょう。

```powershell
$ 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レコード増えること、増えたレコードが登録時と一致すること、不正なパラメータの時に生成できないことを確認します。

```diff:spec/requests/v1/posts_controller.rb
+  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がオーバーライドされてテストが正常に動かない状態となりました…解消までかなり時間がかかりました:sweat_smile:

`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を実装します。

```diff:app/controllers/v1/posts_controller.rb
...

     def create
-      # TODO
+      post = Post.new(post_params)
+      if post.save
+        render json: { post: post }
+      else
+        render json: { errors: post.errors }
+      end
    end
...
```

```powershell
$ 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](https://qiita.com/k-penguin-sato/items/adba7a1a1ecc3582a9c9)

# 続き

→[Rails 6で認証認可入り掲示板APIを構築する #7 update, destroy実装](https://qiita.com/rf_p/items/da70d93e821a44cea702)

【[連載目次へ](https://qiita.com/rf_p/items/a846553c11571f4177eb#%E7%9B%AE%E6%AC%A1)】