I'm trying to follow Mike Hartl's tutorial with RSpec. I've reached the password reset integration test, and so far I was doing well. Then I got to the line that said user = assigns(:user)
I searched for an answer so here is what I have and my error.
Failures:
PasswordResets Password resets email input valid email sends password and redirects to root
Failure/Error: expect(assigns(:user)).to eq([user])
NameError:
undefined local variable or method `user' for #<RSpec::ExampleGroups::PasswordResets::PasswordResets::EmailInput:0x007fc5d16b0ca8>
# ./spec/requests/password_resets_spec.rb:26:in `block (4 levels) in <top (required)>'
require 'rails_helper'
RSpec.describe "PasswordResets", type: :request do
describe "Password resets" do
before do
ActionMailer::Base.deliveries.clear
#valid_user = create(:user)
end
describe "unsuccessful password reset" do
it "flashes danger when email is blank" do
get new_password_reset_path(#valid_user.id)
post password_resets_path, params: { password_reset: { email: " " } }
expect(flash[:danger]).to be_present
expect(page).to render_template(:new)
end
end
describe "email input" do
it "valid email sends password and redirects to root" do
post password_resets_path, params: { password_reset: { email: #valid_user.email } }
expect(#valid_user.reset_digest).not_to match (#valid_user.reload.reset_digest)
expect(ActionMailer::Base.deliveries.size).to eq(1)
expect(flash[:info]).to be_present
expect(page).to redirect_to root_url
expect(assigns(:user)).to eq([user])
end
end
end
end`
The tutorial I'm following https://www.railstutorial.org/book/password_reset 12.18
I'm not sure what else to do.
Your test is erroring because you haven't defined a user variable at that point in your test. You have defined #valid_user. My guess is your test would pass if you change:
expect(assigns(:user)).to eq([user])
to
expect(assigns(:user)).to eq(#valid_user)
Related
I am trying to write a rspec for sessions controller. But i am not able to do so.
I wanted to test controller for valid and invalid valid attributes. Tests should pass if valid attributes are passed for a particular user and should fail if invalid attributes are passed for a given user.
require 'rails_helper'
RSpec.describe SessionsController, type: :controller do
let(:user) { User.create(name: "some",email: 'other#example.com', password: 'rous',password_confirmation: "some") }
describe "get login path" do
it "login page" do
get :new
expect(response).to render_template('sessions/new')
end
end
describe "valid attributes" do
it "create a session" do
post :create, session: { email: user.email, password: user.password }
expect(response.status).to render_template('/users/#user.id')
end
end
describe "invalid attributes" do
it "does not create a session" do
post :create, session: { email: user.email, password: "12345" }
expect(response.status).to render_template('sessions/new')
end
end
describe "does not create a session" do
it "if email is empty" do
post :create, session: {password: "some"}
expect(response.status).to render_template('sessions/new')
end
it "if password is empty" do
post :create, session: {email: "some#gmail.com"}
expect(response.status).to render_template('sessions/new')
end
end
end
Describe "invalid attributes" and "valid attributes" are not working fine.
If email/password are wrong, user redirected to session#new page.
If email & password are correct, user should to redirected to user_path i.e., users show page.
You should really use a request or feature spec instead here as they drive the full stack. You not only want to test that your controller redirects somewhere - you really want to test that redirects somewhere and signs a user in. Preferably without poking inside the internals or mocking the actual authentication process that you're supposed to be testing in the first place.
Both the RSpec and Rails teams disencourage writing controller tests/specs for new applications. Test the behaviour of your application. Not how it does its job.
require "rails_helper"
RSpec.feature "User sign in", type: :feature do
let!(:user) { FactoryBot.create(:user) }
scenario "User signs in with valid crentials" do
visit "/sessions/new"
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign In"
expect(page).to have_text "You have been signed in"
end
scenario "User signs in with invalid crentials" do
visit "/sessions/new"
fill_in "Email", with: user.email
fill_in "Password", with: "nottherightpassword"
click_button "Sign In"
expect(page).to_not have_text "You have been signed in"
expect(page).to have_text "Invalid email or password"
end
end
Or as a lower level request spec:
require "rails_helper"
RSpec.describe "Sessions", type: :request do
let!(:user) { FactoryBot.create(:user) }
describe "POST /sessions" do
context "with valid credentials" do
it "redirects the user" do
post '/sessions',
params: { session: { email: user.email, password: user.password } }
expect(response).to redirect_to user_path(user)
follow_redirect!
expect(response.body).to include("You have been signed in")
end
end
context "with invalid credentials" do
it "does not redirect the user" do
post '/sessions',
params: { session: { email: user.email, password: "nottherightpassword" } }
expect(response).to_not redirect_to user_path(user)
expect(response.body).to include("Invalid email or password")
end
end
end
end
I'm using Rails 6 to work through the Hartl Tutorial, using mostly the online version. I'm in Chapter 10 (which doesn't correspond to the book version chapter numbering)...
https://www.railstutorial.org/book/updating_and_deleting_users
In Section 10.2.2, after Listing 10.25, the Rails Test should be green, but I get these 2 FAIL messages:
FAIL["test_should_redirect_edit_when_not_logged_in", #, 11.729448000027332]
test_should_redirect_edit_when_not_logged_in#UsersControllerTest (11.73s)
Expected true to be nil or false
test/controllers/users_controller_test.rb:18:in `block in '
FAIL["test_should_redirect_update_when_not_logged_in", #, 1.2034909999929368]
test_should_redirect_update_when_not_logged_in#UsersControllerTest (1.20s)
Expected true to be nil or false
test/controllers/users_controller_test.rb:26:in `block in '
The related code is this:
test "should redirect edit when not logged in" do
log_in_as(#other_user)
get edit_user_path(#user)
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when not logged in" do
log_in_as(#other_user)
patch user_path(#user), params: { user: { name: #user.name,
email: #user.email } }
assert_not flash.empty?
assert_redirected_to login_url
end
It's the log_in_as(#other_user) that appears to be the problem, but it could be elsewhere. I can't see why this section has a problem when the similar one in users_edit_test.rb does not.
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 writing RSpec tests for my SessionsController. My tests work fine when testing session#create with valid credentials. However, I want to also write tests for what happens when the users credentials are invalid, such as redirecting back to the sign in page, setting a flash alert, etc. But for any of these tests, I'm getting an error:
1) SessionsController POST #create when password is INCORRECT
Failure/Error: post :create, user: {username: 'Example', password: ''}
ArgumentError:
uncaught throw :warden
# ./spec/support/default_params.rb:7:in `process_with_default_params'
# ./spec/controllers/sessions_controller_spec.rb:24:in `block (4 levels) in <top (required)>'
Here's my sessions_controller_spec.rb code:
require 'spec_helper'
describe SessionsController do
before do
#request.env["devise.mapping"] = Devise.mappings[:user]
end
describe 'POST #create' do
context "when password is INCORRECT" do
let!(:user) { FactoryGirl.create(:user, username: 'Example', password: 'Secr3t&$') }
before(:each) do
post :create, user: { username: 'Example', password: '' }
end
it { should set_the_flash[:alert].to('Invalid username or password.') }
it { should respond_with(:redirect) }
it { should redirect_to(:new_user_session) }
end
end
Here's my spec_helper.rb code:
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
end
Any help would be much appreciated. Thanks!
I know it is too late now but in case this helps someone else: you need to add setup_controller_for_warden to your before block, so it becomes:
before do
#request.env["devise.mapping"] = Devise.mappings[:user]
setup_controller_for_warden
end
Also it would be better to use the translation for you assertion. In rspec 3 format, the assertion should like this:
expect(flash[:alert]).to eq(I18n.t('devise.failure.invalid'))
Testing filling in the contact form without filling in one of the required fields in one test and then filling in the honey_pot field in another test. The tests when valid info is filled in are passing, but the two other specs are failing.
Update I've been running the test a couple of times and sometimes all the tests pass. Other times, the same specs fail.
Terminal output
3) Pages Home page it should behave like all static pages Footer Contact Form when honeypot is filled does not send an email
Failure/Error: expect(ActionMailer::Base.deliveries).to be_empty
expected empty? to return true, got false
Shared Example Group: "all static pages" called from ./spec/requests/pages_spec.rb:69
# ./spec/requests/pages_spec.rb:46:in `block (6 levels) in <top (required)>'
4) Pages Home page it should behave like all static pages Footer Contact Form when fields are not filled does not send an email
Failure/Error: expect(ActionMailer::Base.deliveries).to be_empty
expected empty? to return true, got false
Shared Example Group: "all static pages" called from ./spec/requests/pages_spec.rb:69
# ./spec/requests/pages_spec.rb:38:in `block (6 levels) in <top (required)>'
pages_spec.rb
require 'spec_helper'
describe "Pages" do
subject { page }
shared_examples_for "all static pages" do |path_name|
before { visit send(path_name) }
describe "Footer" do
describe "Contact Form" do
it { should have_selector('h7', text: 'Contact Us') }
context "when a valid message" do
it "sends an email" do
post contact_create_path, message: attributes_for(:message)
expect(ActionMailer::Base.deliveries.last.to).to eq(["#{ENV["MVP_USERNAME"]}"])
ActionMailer::Base.deliveries.clear
end
end
context "when fields are not filled" do
it "does not send an email" do
post contact_create_path, message: attributes_for(:message, name: '', body: '')
expect(ActionMailer::Base.deliveries).to be_empty
ActionMailer::Base.deliveries.clear
end
end
end
context "when honeypot is filled" do
it "does not send an email" do
post contact_create_path, message: attributes_for(:message, sweet_honey: 'bot')
expect(ActionMailer::Base.deliveries).to be_empty
ActionMailer::Base.deliveries.clear
end
end
end
end
end
describe "Home page" do
before { visit root_path }
it_should_behave_like "all static pages", :root_path
it { should have_text('Quality Cars') }
it { should have_title('Misawa Used Cars - Misawa Auto Sales') }
describe "Send a message" do
before do
fill_in "Name", with: 'name'
fill_in "Email", with: 'email#example.com'
fill_in "Phone", with: '999-9999-9999'
fill_in "Body", with: 'Hello'
click_button "Send"
end
describe "after the message is sent" do
it "should render the desired page with a flash" do
expect(page).to have_text('Quality Cars')
expect(page).to have_title('Misawa Used Cars - Misawa Auto Sales')
expect(page).to have_selector('.alert-box.success')
end
end
end
end
end
I was able to get the specs passing by clearing the ActionMailer::Base.deliveries before and after each of the specs where I tested the deliveries. I also rewrote the tests to make it DRYer using the before(:each) and after(:each) methods describe in Rspec's documentation. Update Even better, around(:each)
Improved Specs
describe "Contact Form" do
it { should have_selector('h7', text: 'Contact Us') }
describe "send a message" do
around(:each) { ActionMailer::Base.deliveries.clear }
context "when a valid message" do
it "sends an email" do
post contact_create_path, message: attributes_for(:message)
expect(ActionMailer::Base.deliveries.last.to).to eq(["#{ENV["MVP_USERNAME"]}"])
end
end
context "when fields are not filled" do
it "does not send an email" do
post contact_create_path, message: attributes_for(:message, name: '', body: '')
expect(ActionMailer::Base.deliveries).to be_empty
end
end
context "when honeypot is filled" do
it "does not send an email" do
post contact_create_path, message: attributes_for(:message, sweet_honey: 'bot')
expect(ActionMailer::Base.deliveries).to be_empty
end
end
end
end