アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

rspec の change マッチャで assigns が nil になる

change マッチャの挙動をきちんと理解していなくてつまづきました。

class User < ActiveRecord::Base
  has_many :blogs
end
class Blog < ActiveRecord::Base
  belongs_to :user
end
class UsersController < ApplicationController
  def index
    @user = User.new(name: 'popo')
    @user.save

    b = Blog.new(title: 'mudotaosenai', user_id: @user.id)
    b.save

    render nothing: true
  end
end

といったソースがあるとして(かなり適当です、ごめんなさい) 最初は spec をこう書きました。

require 'rails_helper'

RSpec.describe UsersController, :type => :controller do
  it 'assigins(:user)' do
    get :index
    expect(assigns(:user)).to eq User.last
  end

  it 'blogs.count' do
    expect {
      get :index
    }.to change { assigns(:user).blogs.count }.by(1)
  end
end

assigns(:user) は Green ですが blogs.count は Red です。

Failures:

  1) UsersController blogs.count
     Failure/Error: }.to change { assigns(:user).blogs.count }.from(0).to(1)
     NoMethodError:
       undefined method `blogs' for nil:NilClass

最初はなんで assings(:user) が nil になるのか分からなかったのですが、change メソッドはブロック内に記述した処理の実施前、実施後に値を取得しに行くことを理解し原因がはっきりしました。

このケースでは expect { get: index } とあるので、 get: index が実行される前に一度 @user の値を取得しようとしますが、ruby ではインスタンス変数は呼び出されて初めてセットされるので、get: index の実施前は @user は nil となります。

例があまり良くないですが、 change メソッドでは assigns は nil になるといったことでした。

参考にした記事

change matcher