この記事で書くこと
rspec-railsでmockを使用してテストする方法をまとめました。ただメソッドなどを紹介するというよりは、対象のコードがいて、それをテストするといった少しだけ実践的な方法で紹介します。
使用したコード
今回はcontroller specを使用します。(現在は、request specに移行することが推奨されているみたいです。)
テスト対象とテストコードは以下になります!
① テスト対象のコード。
・・・
def create
data = request.body.string
user = User.new
if user.change_allelgy_status(data) == 0 then
render nothing: true, status: 200
elsif
render nothing: true, status: 400
end
end
・・・
② テストコード。
require 'rails_helper'
RSpec.describe UsersController, type: :controller do
describe "POST #create" do
context "when the allergy status change process is successful" do
it "has 200 status code." do
user = instance_double(User)
allow(User).to receive(:new).and_return(user)
allow(user).to receive(:change_allelgy_status).and_return(0)
post :create
expect(response).to have_http_status(200)
end
end
end
end
解説
1. インスタンスmockの作成
user = instance_double(User)
Userクラスのインスタンスmockを作成しています。これを作成することで、user.change_allelgy_status
のようなインスタンスメソッドをmockから呼び出すことができます。
また、似たような処理にUser = class_double(User)
がありますが、これはUser.new
のようにクラスメソッドmockから呼び出したい時に使用します。
2. スタブの作成
今回、個人的に1番ハマった処理です。まず、上手くいったコードからご紹介します。
allow(User).to receive(:new).and_return(user)
allow(user).to receive(:change_allelgy_status).and_return(0)
これは、user = User.new
とuser.change_allelgy_status
をそれぞれスタブにしたものです。
allow(User).to receive(:new).and_return(user)
は、Userクラスのnewメソッドが呼ばれた時に、user(インスタンスmock)を返すという意味です。
user.change_allelgy_status
は、userインスタンスのchange_allelgy_statusメソッドが呼ばれた時に、0を返すという意味です。以下の分岐に入るように返り値を0にしています。
if user.change_allelgy_status(data) == 0 then
render nothing: true, status: 200 # <= ここの分岐!!
elsif
以上が正しいコードでした。それでは、次に僕がハマったパターンを紹介します。
allow(user).to receive(:change_allelgy_status).and_return(0)
はじめ、上記のコードだけ書いてテストしたところ、なぜかスタブのchange_allelgy_status
が呼ばれずに、実処理が実行されてしまいました。この原因は、おそらく下記だと思います。
・・・
def create
data = request.body.string
user = User.new
# <= ここでuserインスタンスを作成しているため、次の処理で呼ばれる
# user.cahnge_allelgy_statusは、このuserインスタンスが使用される。
if user.change_allelgy_status(data) == 0 then
render nothing: true, status: 200
elsif
render nothing: true, status: 400
end
end
・・・
従って、User.new
から作成したuserインスタンスをスタブ化し、スタブ化されたインスタンスからchange_allelgy_statusを呼び出すことになりました。
allow(User).to receive(:new).and_return(user)
allow(user).to receive(:change_allelgy_status).and_return(0)
3. 期待値の確認
post :create
createアクションにpostリクエストを投げて、処理を開始します。
expect(response).to have_http_status(200)
postリクエストのレスポンスコードが200であることを確認しています。
まとめ
簡単ではありますが、rspecでmockを使用したテストをご紹介しました。実際に自分がつまづいた点も交えて記載したので、参考にしてもらえたら幸いです。