Reputation: 530
I'm working through the treehouse tutorials using Ruby 2.1.2 and Rails 4.1.5 and I got into an issue with my tests in the auth tutorial.
The tutorial says that this code should pass:
def create
user = User.find(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to todo_lists_path
else
flash.now[:error] = "There was an error logging in. Please check your email and password"
render action: 'new'
end
end
Here are my tests:
describe "POST 'create'" do
context "with correct credentials" do
let!(:user){
User.create(
first_name: "Jason",
last_name: "Seifer",
email: "[email protected]",
password: "teamtreehouse1234",
password_confirmation:"teamtreehouse1234")}
it "redirects to the todo list path" do
post :create, email: "[email protected]", password: "treehouse1234"
expect(response).to be_redirect
expect(response).to redirect_to(todo_lists_path)
end
it "finds the user" do
expect(User).to receive(:find_by).with({email: "[email protected]"}).and_return(user)
post :create, email: "[email protected]", password: "treehouse1234"
end
it "authenticates the user" do
User.stub(:find_by).and_return(user)
expect(user).to receive(:authenticate)
post :create, email: "[email protected]", password: "treehouse1234"
end
it "sets the user id in the session" do
post :create, email: "[email protected]", password: "treehouse1234"
expect(session[:user_id]).to eq(user.id)
end
it "sets the flash success messagge" do
post :create, email: "[email protected]", password: "treehouse1234"
expect(flash[:success]).to eq("Thanks for logging in!")
end
end
context "with blank credentials" do
it "renders the new template" do
post :create
expect(response).to render_template('new')
end
it "sets the flash error messagge" do
post :create, email: "[email protected]", password: "treehouse1234"
expect(flash[:error]).to eq("There was an error logging in. Please check your email and password")
end
end
context "with incorrect credentials" do
let!(:user){
User.create(
first_name: "Jason",
last_name: "Seifer",
email: "[email protected]",
password: "teamtreehouse1234",
password_confirmation:"teamtreehouse1234")}
it "renders the new template" do
post :create, email: user.email, password: "fuckingpassword"
expect(response).to render_template('new')
end
it "sets the flash error messagge" do
post :create, email: user.email, password: "fuckingpassword"
expect(flash[:error]).to eq("There was an error logging in. Please check your email and password")
end
end
end
That gives me the error undefined method `authenticate' for nil:NilClass
the only way I've made the tests pass is with the following:
class UserSessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:email])
if user.nil?
flash[:error] = "There was an error logging in. Please check your email and password"
render action: 'new'
else
user.authenticate(params[:password])
session[:user_id] = user.id
flash[:success] = "Thanks for logging in!"
redirect_to todo_lists_path
end
end
end
I know it's ugly but it worked, right to the point that I needed to test whether it denied authentication with the right user but the wrong password. The tests returned two errors:
1) UserSessionsController POST 'create' with incorrect credentials renders the new template
Failure/Error: expect(response).to render_template('new')
expecting <"new"> but rendering with <[]>
2) UserSessionsController POST 'create' with incorrect credentials sets the flash error messagge
Failure/Error: expect(flash[:error]).to eq("There was an error logging in. Please check your email and password")
expected: "There was an error logging in. Please check your email and password"
got: nil
The first example failed because for what ever reason(maybe rspec) the if clause doesn't handle user.authenticate(params[:password]) because it returns a hash.
let it be noted that the following code works in bin/rails console :
> user = User.create(first_name:"John", last_name: "Doe", email: "[email protected]", password: "password", password_confirmation: "password")
> user.save
> if user && user.authenticate("password")
> puts "wawa"
> end
wawa
=> nil
I've tried refactoring the create method so that it authenticates on assignment to no avail:
def create
user = User.find_by(email: params[:email]).authenticate(params[:password])
if user.nil? || user == false
flash[:error] = "There was an error logging in. Please check your email and password"
render action: 'new'
else
session[:user_id] = user.id
flash[:success] = "Thanks for logging in!"
redirect_to todo_lists_path
end
end
I get undefined method `authenticate' for nil:NilClass
again.
Upvotes: 0
Views: 2074
Reputation: 530
Found the bug it wasn't in the logic it was in the tests, I was testing the password with incorrect credentials treehouse123
instead of the one that was being created in my user teamtreehouse123
.
Inspiration came in this debugging code:
def create
user = User.find_by(email: params[:email])
if (user.nil? == false)
if user.authenticate(params[:password])
puts user.authenticate(params[:password])
puts "NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN Batman!"
session[:user_id] = user.id
flash[:success] = "Thanks for logging in!"
redirect_to todo_lists_path
else
puts user.authenticate(params[:password])
puts "waka waka waka waka waka waka waka waka waka waka waka waka waka waka waka waka"
flash[:error] = "There was an error logging in. Please check your email and password"
render action: "new"
end
else
flash[:error] = "There was an error logging in. Please check your email and password"
render action: "new"
end
end
user.authenticate(params[:password])
always returned false, regardless of what I did.
The Treehouse code is correct, but I'm changing all occurrences of teamtreehouse123
to password
.
Upvotes: 1
Reputation: 3371
The undefined method 'authenticate' for nil:NilClass
is a classic Rails error.
The error is never in the authenticate
method. It's always in the part before it.
Here, you have an error on User.find_by(email: params[:email])
. This part of code returns nil
in this case. So Rails is trying to execute nil.authenticate(params[:password])
.
Please check your params[:email]
value or your list of users.
Upvotes: 0