【Rails】RSpec初心者による初心者のためのテストの書き方
はじめに
こんにちは。RSpec5 日目の超初心者です。RSpec のコントローラーのテストについて、自分が初めて行ったコントローラーのテストのコードとともにまとめる記事にしたいと思います。
実装にあたり、たくさんの記事で勉強させていただきました。深く感謝いたします。
今回の記事はテストに関しての基本的な知識を前提としています。以下の記事が大変わかりやすいです。
使える RSpec 入門・その 1「RSpec の基本的な構文や便利な機能を理解する」
また、FactryGirl や Faker がインストール済みの環境で行います。
Rails RSpec の基本 ~導入編~
今回テストを行ったのはメッセージを送信する処理を行う messages_controller です。
users と chat_groups が多対多関係、また、それぞれ messages と1対多関係を持ちます。
class MessagesController < ApplicationController
before_action :set_chat_group, only: %i(index create)
def index
@message = Message.new
end
def create
@message = current_user.messages.new(message_params)
if @message.save
redirect_to chat_group_messages_path
else
flash[:alert] = "メッセージを入力してください。"
render action: :index
end
end
private
def message_params
params.require(:message).permit(:body, :image).merge(chat_group_id: params[:chat_group_id])
end
def set_chat_group
@chat_group = ChatGroup.find(params[:chat_group_id])
end
end
テストの書き方
コントローラーのテストの実装は
① テストケース設計
② テストデータ作成
③ テストロジック作成
④ リファクタリング
の順序で行います。テストの実装手順に関してはこちらの記事を参考にさせていただきました。このように段階的に実装していくことでテストの構造を理解しながら進めていくことができました。
#1.テストケース設計
コントローラーのテストでは、1 つのアクションにつき 2 つの挙動をテストします。
① 正しいビューに変遷するか。
② インスタンス変数(アクション内で@〜で定義している変数)が期待された値を持つか。
つまり、今回のコントローラーのテストでは次のように設計します。
index アクション
describe 'GET #index' do
it "正しいビューに変遷する" do
end
it "@messageが期待される値を持つ" do
end
it "@chat_groupが期待される値を持つ" do
end
end
create アクション
describe 'Post #create' do
context "@messageが保存できた時" do
it "データベースに値が保存される" do
end
it "正しいビューに変遷する" do
end
end
context "@messageが保存できなかった時" do
it "データベースに値が保存されない" do
end
it "正しいビューに変遷する" do
end
end
end
index アクションに関しては describe でのグループ化のみですが、create アクションは少し違っていますね。
messages_controller を見ると、create アクションでは条件分岐があります。条件分岐は、context という機能を用いて、全ての場合についてのテストを行います。context は describe と機能は変わりませんが、「条件分岐のテストである」ことを示すため(可読性を高めるため)、使用します。
2.テストデータ作成
テストデータの作成は以下の2つの手順で行います。
① ダミーデータの作成
② テストコントローラ内で定義
① ダミーデータの作成
FactoryGirl.define do
factory :chat_group do
name Faker::Team.name
end
end
上記のようなファイルを用意することで、テストで利用できるダミーデータを作成します。
カラムの属性によって異なるダミーデータの作成方法はこちらが参考になります。
② テストコントローラ内で定義
index アクション
…
it "@chat_groupが期待される値を持つ" do
chat_group = create(:chat_group)
end
…
chat_group = create(:chat_group)
このように定義することで作成したダミーデータを呼び出せます。今後、この作成したテストデータでテストを行っていきます。
以上のような流れで他の example のテストデータも作成します。
3.テストロジック作成
テストロジックの作成は以下の2つの手順で行います。
①HTTP メソッドの指定
② エクスペクテーションの実装
①HTTP メソッドの指定
index アクション
…
it "@chat_groupが期待される値を持つ" do
chat_group = create(:chat_group)
get :index, chat_group_id: chat_group
end
…
HTTPメソッド :(コントローラーのアクション)
上記のように指定することで擬似的にコントローラーのアクションにリクエストできます。
さらに、パラメータが必要な場合は、ハッシュ形式で渡します。
messages の index アクションのパスを確認すると、chat_group_id をパラメータで持つことがわかります。
そのため、chat_group_id: chat_group
でパラメータを渡すことで、作成した chat_group のパスに擬似的にリクエストできるようになります。
② エクスペクテーションの実装
index アクション
…
it "@chat_groupが期待される値を持つ" do
chat_group = create(:chat_group)
get :index, chat_group_id: chat_group
expect(assigns(:chat_group)).to eq chat_group
end
…
assigns メソッドを用います。assigns メソッドはコントローラーのインスタンス変数をテストするメソッドです。引数にインスタンス変数をシンボル型で渡します。
今回の場合、messages_controller.rb の index アクションで@chat_group というインスタンス変数を定義しているため、これのテストになります。
expect(assigns(:chat_group)).to eq chat_group
すなわち、この一行では、message_controller.rb の index アクションで定義されている@chat_group というインスタンス変数と、テスト内で作成した chat_group というテストデータが確かに同じであることを検証しています。これが同じであれば、インスタンス変数が期待された値を持つと言えることになります。
その他の example に関しても、テストしたい内容によってエクスペクテーションを実装していきます。
4.リファクタリング
let
describe MessagesController do
let(:chat_group) { create(:chat_group) }
describe 'GET #index' do
it "renders the :index template" do
~~~
end
it "assigns the requested messsage to @message" do
~~~
end
it "assigns the requested chat_group to @chat_group" do
get :index, chat_group_id: chat_group
expect(assigns(:chat_group)).to eq chat_group
end
end
end
let でテストデータを一度定義してしまえば、それぞれ example でわざわざテストデータを定義する必要がなくなります。コード量が減らせ、可読性も高まるためテストデータを定義する際は let を用いたいですね。
そのほかにも様々なリファクタリングの方法があるようです。知っておけば量が少なく、可読性が高まるコードが書けるので紹介した記事などを参考にしてみてください。
最後に
以上のように段階的に分解して考えていくことで、少しずつテストを理解できました。間違っている点や改善点がありましたらご指摘お願いします。
Discussion