rspecのmockでつまずいた時の話

  • このエントリーをはてなブックマークに追加
rspec-mock

rspec-railsでmockを使用してテストする方法をまとめました。ただメソッドなどを紹介するというよりは、対象のコードがいて、それをテストするといった少しだけ実践的な方法で紹介します。

使用したコード

今回はcontroller specを使用します。(現在は、request specに移行することが推奨されているみたいです。)
テスト対象とテストコードは以下になります!

① テスト対象のコード。

app/controller/users_controller.rb

・・・
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
・・・

② テストコード。

spec/controllers/users_controller_spec.rb

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が呼ばれずに、実処理が実行されてしまいました。この原因は、おそらく下記だと思います。

app/controller/users_controller.rb

・・・
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を使用したテストをご紹介しました。実際に自分がつまづいた点も交えて記載したので、参考にしてもらえたら幸いです。

  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。

コメントを残す

*