Load to Professional...

AWSエンジニアの日常。地に足をつける。

多重配列付近でNoMethodError for NilClassが出たとき

Webアプリ開発中に、controllerのunit test作成中にNoMethodError for NilClassが出て詰まったためメモ。

 

環境

・コントローラ (session_cotroller)

~略〜
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
if user.admin
redirect_to admin_url
else
redirect_to root_url
end
else
flash.now[:danger] = "invalid email/password, failed to login"
render 'new'
end
end
~略〜
 
上記のコントローラに対して、以下のようなテストを記述した
 
・spec/controllers/sessions_controller_spec.rb
describe "#create" do

context "as correct user" do
let(:user) { FactoryBot.create(:user) }
let(:valid_parameters) do
{ email: user.email, password: user.password }
end

it "success to log in" do
post :create, session: valid_parameters }
expect(session[:user_id]).to eq user.id
end
end
end
 
すると、こんなエラーが。。。。
 

Failure/Error: user = User.find_by(email: params[:session][:email].downcase)

 

 NoMethodError:

 undefined method `[]' for nil:NilClass

 # ./app/controllers/sessions_controller.rb:10:in `create'

 # ./spec/controllers/sessions_controller_spec.rb:33:in `block (4 levels) in <main>'

 


 

エラー文の内容から、params[:session]がnilになってるのかな?とは推測できましたが、その原因がrspec側にあると考えず、controller側の修正を頑張ってしまうという見当違いなことをしてしまった。。。

 

解決策 

調べているうちに、この記事に出会う。

 https://teratail.com/questions/44554

 

上記記事によると、答えは単純明快。

コントローラ側は、

params[:session][:email]

と多重配列で値を渡しているが、

RSpec側は、

sessions[:email]

としてしまい、エラー。

 

paramsに値を入れられていないのだからnilになるのは当然ですよね、って話だった。

 

原因
user = User.find_by(email: params[:session][:email].downcase)

 

 これの意味を理解してかけていなかったことがそもそもの原因

 

教訓

・「理解できるコード」だけ書くようにしろ、を守れ。

・controllerで渡す値とテストで渡す値はANDになってますよね、って確認しよう。