I have an app that uses Sorcery (or tries to use it),
and I am writing the specs for it:
context "successfull attempts to log in" do
let(:attr) { attributes_for(:credentials) }
before(:each) do
#user = create(:user, attr)
end
it "should log the user in" do
post :create, attr.merge(remember_me: false)
controller.should be_logged_in
end
end
Here is the FactoryGirl factory:
FactoryGirl.define do
factory :user do
email Faker::Internet.safe_email
password "password"
password_confirmation { |u| u.password }
client_id 1
end
factory :credentials, class: User do
email "user#example.com"
password "password"
end
end
And here is the controller action:
class SessionsController < ApplicationController
# ...
def create
login(params[:email], params[:email], params[:remember_me])
flash.now[:error] = "Invalid email/password combination"
render :new
end
end
The error message is the following:
1) SessionsController POST 'create' successfull attempts to log in should log the user in
Failure/Error: controller.should be_logged_in
expected logged_in? to return true, got false
# ./spec/controllers/sessions_controller_spec.rb:54:in `block (4 levels) in <top (required)>'
The spec keeps failing for some reason. Could anyone explain to me why please?
Your controller uses the email as both the username and password, which looks like a copy and paste error. Is that correct?
Related
I'm attempting to create some controller specs in my Rails 5 app using rspec, but the code keeps throwing the following error:
1) SessionsController Log in and log out logs in with valid user
Failure/Error: user = User.find_by(email: params[:session][:email].downcase)
NoMethodError:
undefined method `[]' for nil:NilClass
My spec is pretty straightforward. The user instance variable uses factory-girl to create a user with the email "user#example.com" and password as "password." When I call puts on these variables, I can see that they are set correctly.:
require 'rails_helper'
RSpec.describe SessionsController, type: :controller do
before :each do
#user = create(:user)
end
describe "Log in and log out" do
before :each do
post :create, { session: { email: #user.email,
password: #user.password }}
end
it "logs in with valid user" do
puts #user.email + " " + #user.password
expect(is_logged_in?).to be_truthy
end
end
end
Finally, the code from the sessions controller is below:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
remember user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
end
Am I misunderstanding the way params are sent to the controller in rspec? Is there any other reason for this error to be returned?
Changes were made in Rails 5 to the way you send params in controller tests.
Instead of:
before :each do
post :create, { session: { email: #user.email,
password: #user.password }}
end
You need to provide the params key in post request attribute hash. Like so...
before :each do
post :create, params: { session: { email: #user.email,
password: #user.password }}
end
It's subtle, but it's different. Let me know if that works.
I am trying to validate that the current_user's organization matches that of the organization they are trying to view.
Here's the part of the controller that's failing this test (#organization is being defined in an earlier method):
if current_user.organization != #organization
redirect_to root_path, notice: "Not authorized to edit this organization"
end
Here's the failing test:
require 'rails_helper'
RSpec.describe Admin::PagesController, :type => :controller do
describe 'GET #home' do
login_user
before do
#organization = FactoryGirl.create(:organization)
end
context "valid params" do
it "renders the home template and returns http 200" do
get :home, name: #organization.name
expect(response).to render_template("home")
expect(response.status).to eq(200)
end
end
end
Here's my factory:
factory :user do
email { Faker::Internet.email }
organization_id 1
password "foobarfoobar"
password_confirmation { |u| u.password }
end
...And here's where login_user is being defined:
module ControllerMacros
def login_user
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
sign_in user
end
end
Stacktrace:
1) Admin::PagesController GET #home valid params renders the home template and returns http 200
Failure/Error: it "renders the home template and returns http 200" do
expecting <"home"> but rendering with <[]>
# ./spec/controllers/admin/pages_controller_spec.rb:15:in `block (4 levels) in <top (required)>'
However:
[2] pry(#<RSpec::ExampleGroups::AdminPagesController::GETHome::ValidParams>)> subject.current_user.organization == #organization
=> true
Not sure what is going wrong here, seems like pretty standard stuff. Any ideas?
Turns out the issue was that I was sending in the wrong parameter - should have been sending #organization.subdomain, not #organization.name. :(
I'm struggling with how to correctly test the POST #create action in the SessionsController. I've attempted to stub authentication. My assumption is that a valid test should test that the session[:user_id] is in fact equal to the #user.id value, after the post :create action is executed. However, I get nil returned for session[:user_id].
Mocks, stubs, etc are still a bit new to me. The stub for authentication seems pretty straightforward. However, why can't I get the session value to be returned when I run this test?
The test for valid authentication is currently failing (though it works in actual practice - i.e. I can sign in to the app). This is the one I'm concerned with. The second test (invalid password) is passing, and seems fine. All of my other session controller specs are passing.
Here's the portion of my sessions_controller_spec.rb file that deals with the session creation via POST #create:
require 'rails_helper'
describe SessionsController, type: :controller do
describe "POST #create" do
context "where authentication is valid" do
it "creates a new session with a welcome message" do
#user = create(User, id: 1)
allow(#user).to receive(:authenticate).and_return #user
post :create, email: "test#example.com", password: "secret1234"
expect(session[:user_id]).to eq #user.id
expect(flash[:notice]).to match("Welcome back!")
end
end
context "where password is invalid" do
it "re-renders the signin page with an alert message" do
user = create(:user)
post :create, session: { email: user.email, password: 'invalid' }
expect(response).to render_template :new
expect(flash[:alert]).to match("Incorrect email/password combination!")
end
end
end
# CODE FOR OTHER TESTS OMITTED
end
This is my sessions_controller.rb file:
class SessionsController < ApplicationController
def new
end
def create
if user = User.authenticate(params[:email], params[:password])
session[:user_id] = user.id
flash[:notice] = "Welcome back!"
redirect_to root_path
else
flash.now[:alert] = "Incorrect email/password combination!"
render :new
end
end
def destroy
session[:user_id] = nil
redirect_to new_session_path, notice: "Signed Out!"
end
end
The test returns the following error:
1) SessionsController POST #create where authentication is valid creates a new session with a welcome message
Failure/Error: expect(session[:user_id]).to eq #user.id
expected: 1
got: nil
(compared using ==)
# ./spec/controllers/sessions_controller_spec.rb:12:in `block (4 levels) in <top (required)>'
# ./spec/rails_helper.rb:42:in `block (3 levels) in <top (required)>'
# ./spec/rails_helper.rb:41:in `block (2 levels) in <top (required)>'
Is there a better, or preferred, way to test that the SessionsController successfully creates the session and sets the session[:user_id] value?
Comments, code critiques, etc, are welcome.
You didn't stub the method correctly:
allow(#user).to receive(:authenticate).and_return #user
In the controller you are calling authenticate on a class, not the instance. The line above should be:
allow(User).to receive(:authenticate).and_return #user
Been trying to figure this out for a while now, but can't seem to go green. Here is the rspec error that I am getting:
1) UsersController POST create with valid parameters creates a new user
Failure/Error: expect {post :create, user}.to change(User, :count).by 1
count should have been changed by 1, but was changed by 0
# ./spec/controllers/users_controllers_spec.rb:36:in `block (4 levels) in <top
(required)>'
Here is my relevant test in the users_controllers_spec:
describe UsersController do
let(:user) {FactoryGirl.create :user}
context 'POST create' do
context 'with valid parameters' do
before {post :create, user}
let(:user) {FactoryGirl.build :user}
it 'creates a new user' do
expect {post :create, user}.to change(User, :count).by 1
end
it {should respond_with 200}
end
And here is my users_controller create method:
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome #{#user.name.capitalize}! Please complete your profile."
redirect_to profile_path(current_user)
else
render :new
end
end
Also, here is my factories.rb file:
FactoryGirl.define do
factory :user do
name "John"
email "John#example.com"
password "password"
password_confirmation "password"
end
end
What am I missing? I feel like it is something really obvious, but I can't seem to figure it out. Any help would be much appreciated.
Your test involves creating a second user with FactoryGirl, but the creation of the second user is failing because the email is the same as the first and you have a uniqueness validation on that attribute.
You can either not create the first user at all or you'll need to make the emails different (e.g. by passing in an email parameter to your FactoryGirl call or changing the Factory to make each email address unique).
I've just started using rails, and decided to follow the "Ruby on Rails Tutorial" by M. Hartl. Seems like a good intro.
Am running into a failed test that's driving me nuts.
I am running rails 3.1.1, with rspec 2.7.0
I have tried modifying the condition, and tests on the "has_password" method work.
The failing test:
1) User password encryption authenticate method should return the user on email/password match
Failure/Error: matching_user.should == #user
expected: #
got: nil (using ==)
# ./spec/models/user_spec.rb:149:in `block (4 levels) in '
The rspec test:
describe User do
before(:each) do
#attr = {:name => 'testing',
:email =>'testing#example.com',
:password => "testtest",
:password_confirmation => "testtest"}
end
...
describe "password encryption" do
before(:each) do
#user = User.create!(#attr)
end
...
describe "authenticate method" do
it "should exist" do
User.should respond_to(:authenticate)
end
it "should return nil on email/password mismatch" do
User.authenticate(#attr[:email], "wrongpass").should be_nil
end
it "should return nil for an email address with no user" do
User.authenticate("bar#foo.com", #attr[:password]).should be_nil
end
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
matching_user.should == #user
end
end
In the User model:
...
def has_password?(submitted_password)
encrypt_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email) #self.where("email = ?", email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
I cannot figure out what I'm doing wrong here.
In your failing spec you have
matching_user.should == #user
But #user isn't defined anywhere so it's set to nil.
Edit:
Try adding the following puts into the failing spec and see what results you get in your spec output after running it.
it "should return the user on email/password match" do
matching_user = User.authenticate(#attr[:email], #attr[:password])
puts matching_user # add this
puts #user # and also this
matching_user.should == #user
end