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
Related
I've got custom member_action in my Active Admin panel which is responsible for resending devise reset password instructions.
admin/users.rb
ActiveAdmin.register User do
member_action :reset_password do
user = User.find(params[:id])
user.send_reset_password_instructions
redirect_to(admin_user_path(user),
notice: "Password reset email sent to #{user.email}")
end
end
How to write RSpec tests for such an action? The only thing I found is this one and I think it's not quite related to my problem.
I was trying to sth like below:
require 'rails_helper'
describe Admin::UsersController, type: :controller do
include Devise::TestHelpers
let!(:admin) { create(:admin_user) }
before(:each) do
sign_in admin
end
describe 'GET user' do
let(:user) { create(:user, :random_email) }
before(:each) do
User.should_receive(:find).at_least(:once).and_return(user)
get :show
end
it 'sends email' do
get :reset_password
expect(user).should_receive(:send_reset_password_instructions)
end
end
end
But I'm getting an error:
ActionController::UrlGenerationError:
No route matches {:action=>"reset_password", :controller=>"admin/users"}
Personally I prefer to use a feature test, since when using active admin, UI stuff handle by the framework:
RSpec.feature 'Reset Password', type: :feature do
let(:user) { create :user }
before do
login_as(user, scope: :user)
end
scenario 'can delete future episode' do
visit some_path
click_link 'Reset Password'
expect(page.current_path).to eq(admin_user_path(user))
expect(page).to have_content("Password reset email sent to #{user.email}")
end
end
Ok, it turns out small adjustments (pass the user.id in params) make the trick.
describe Admin::UsersController, type: :controller do
include Devise::Test::ControllerHelpers
before { sign_in admin }
let!(:admin) { create(:admin_user) }
describe 'GET user' do
let(:user) { create(:user, :random_email) }
before do
allow(User).to receive(:find).at_least(:once) { user }
get :show, params: { id: user.id }
end
it 'sends email' do
get :reset_password, params: { id: user.id }
expect(flash[:notice]).to match("Password reset email sent to #{user.email}")
end
end
end
I'm writing the RSpec tests for the index action for Project. A project belongs_to a client, which belongs_to a user.
What I'm trying to do is create 30 instances of project to be used in pagination. All belonging to the same user.
When I run the tests, I'm giving the following error (I've tried rake db test:prepare):
Failure/Error: before(:all) { #projects = FactoryGirl.create_list(:project, 30, user: user) }
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
factories.rb
FactoryGirl.define do
factory :user do
name "John Nana"
email "john#nana.com"
password "woopwoop1"
password_confirmation "woopwoop1"
end
factory :client do
name "Widget inc."
user
end
factory :user1 do
name "Bob Inc"
email "lol#catz.com"
password "nayy1"
password_confirmation "nayy1"
end
factory :project do
sequence(:name) { |n| "Project #{n}" }
fee "250"
client
end
end
project_pages_spec.rb
require 'spec_helper'
describe "create" do
let(:user) { FactoryGirl.create(:user) }
let(:submit) { "Create project" }
describe "when signed in" do
before do
capybara_sign_in(user)
#client = user.clients.create(name: "Widget inc.")
visit new_project_path
end
describe "with valid information" do
describe "from /projects" do
before do
visit projects_path
click_link "Add project"
fill_in "Project name", with: "Brochure"
fill_in "Fee", with: "1000"
select(#client.name, from: 'project_client_id')
end
it "should create a project and redirect to /projects" do
expect { click_button submit }.to change(Project, :count).by(1)
current_path.should == '/projects'
end
end
before do
fill_in "Project name", with: "Brochure"
fill_in "Fee", with: "1000"
select(#client.name, from: 'project_client_id')
end
it "should create a project" do
expect { click_button submit }.to change(Project, :count).by(1)
end
end
describe "with invalid information" do
before do
too_long_project = "a" * 50
fill_in "Project name", with: too_long_project
fill_in "Fee", with: "1000000000"
select(#client.name, from: 'project_client_id')
end
it "should not create a project" do
expect { click_button submit }.not_to change(Project, :count)
end
end
end
describe "when not signed in" do
before { visit new_project_path(user) }
it "should redirect to signin path" do
current_path.should == '/signin'
end
end
end
describe "index" do
let(:user) { FactoryGirl.create(:user) }
before do
capybara_sign_in(user)
visit projects_path
end
describe "pagination" do
before(:all) { #projects = FactoryGirl.create_list(:project, 30, user: user) }
after(:all) { Project.delete_all }
it { should have_selector('div.pagination') }
it "should list each user" do
Project.paginate(page: 1).each do |project|
expect(page).to have_selector('li', text: project.name)
end
end
it "should list all projects" do
#projects.each do |p|
expect(page).to have_selector('li', p.name)
end
end
end
end
You can use FactoryGirl's Sequences to generate unique email addresses:
factory :user do
name "John Nana"
sequence(:email) { |n| "person#{n}#example.com" }
password "woopwoop1"
password_confirmation "woopwoop1"
end
My issue is that I have to create a new user and login for each individual capybara test.
An example is below:
require 'spec_helper'
describe "users" do
describe "user registration" do
it "should create a new user and log in" do
# Register a new user to be used during the testing process
visit signup_path
fill_in 'Email', with: 'testuser'
fill_in 'Password', with: 'testpass'
fill_in 'Password confirmation', with: 'testpass'
click_button 'Create User'
current_path.should == root_path
page.should have_content 'Thank you for signing up!'
end
end
describe "user login" do
it "should log in" do
# log in
visit login_path
fill_in 'Email', with: 'testuser'
fill_in 'Password', with: 'testpass'
click_button 'Log In'
current_path.should == root_path
page.should have_content 'Logged in!'
end
end
end
The login test fails because the user no longer exists in the database for that test.
This could be fixed simply by putting both in one test, but I believe that is bad practice.
Also I have another file which currently is registering and logging in between each test using a before_do, which also seems to be quite bad... you can see that code here.
For the record this is my first rails app so perhaps I am trying to do this the wrong way. I would like to dry it up as much as possible..
Is capybara really this bad to use on pages that require user login?
I have done it this way.
require "spec_helper"
describe "Users" do
subject { page }
describe "User Registration" do
before { visit signup_path }
let(:submit) { "Sign up" }
describe "with invalid information" do
it "should not create a user" do
expect { click_button submit }.not_to change(User, :count)
end
end
describe "with valid information" do
before do
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar12"
fill_in "Password confirmation", with: "foobar12"
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after registration" do
before { click_button submit }
it { should have_content 'Thank you for signing up!' }
end
describe "after registration signout and login" do
let(:user) { User.find_by_email('user#example.com') }
before do
click_button submit
visit signout_path
sign_in user # sign_in is a method which u can define in your spec/support/utilities.rb . Define once and use at multiple places.
end
it { should have_content 'Logged In!' }
it { should have_link('Logout') }
end
end
end
end
# spec/support/utilities.rb
def sign_in(user)
visit sign_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Log in"
end
your every describe and it block will run after the before block in parent that's why we need to click_button in every block in above test cases.
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
I have devise authentication and registration set up on my Rails app. I'm using after_sign_in_path_for() to customise the redirect when the user signs in based on various scenarios.
What I'm asking is how to test this method? It seems hard to isolate since it is called automatically by Devise when the user signes in. I want to do something like this:
describe ApplicationController do
describe "after_sign_in_path_for" do
before :each do
#user = Factory :user
#listing = Factory :listing
sign_in #user
end
describe "with listing_id on the session" do
before :each do
session[:listing_id] = #listing.id
end
describe "and a user in one team" do
it "should save the listing from the session" do
expect {
ApplicationController.new.after_sign_in_path_for(#user)
}.to change(ListingStore, :count).by(1)
end
it "should return the path to the users team page" do
ApplicationController.new.after_sign_in_path_for(#user).should eq team_path(#user.team)
end
end
end
end
end
but that's obviously not the way to do it because I just get an error:
Failure/Error: ApplicationController.new.after_sign_in_path_for(#user)
RuntimeError:
ActionController::Metal#session delegated to #_request.session, but #_request is nil: #<ApplicationController:0x00000104581c68 #_routes=nil, #_action_has_layout=true, #_view_context_class=nil, #_headers={"Content-Type"=>"text/html"}, #_status=200, #_request=nil, #_response=nil>
So, how can I test this method?
Oddly, I was wondering this very thing today. Here's what I came up with. I created an anonymous subclass of ApplicationController. In this anonymous subclass, I exposed the protected methods that I wanted to test as public methods. Then I tested them directly.
describe ApplicationController do
controller do
def after_sign_in_path_for(resource)
super resource
end
end
before (:each) do
#user = FactoryGirl.create(:user)
end
describe "After sigin-in" do
it "redirects to the /jobs page" do
controller.after_sign_in_path_for(#user).should == jobs_path
end
end
end
On a similar note - if you want to test the redirect after sign-up, you have two options.
First, you can follow a pattern similar to above and very directly test the method in RegistrationsController:
require 'spec_helper'
describe RegistrationsController do
controller(RegistrationsController) do
def after_sign_up_path_for(resource)
super resource
end
end
describe "After sign-up" do
it "redirects to the /organizations/new page" do
#user = FactoryGirl.build(:user)
controller.after_sign_up_path_for(#user).should == new_organization_path
end
end
end
Or, you can take a more integration-testing sort of approach and do the following:
require 'spec_helper'
describe RegistrationsController do
describe "After successfully completing the sign-up form" do
before do
#request.env["devise.mapping"] = Devise.mappings[:user]
end
it "redirects to the new organization page" do
post :create, :user => {"name" => "Test User", "email" => "test#example.com", "password" => "please"}
response.should redirect_to(new_organization_path)
end
end
end
For the newcomers, I would recommend doing this way:
RSpec.describe ApplicationController, type: :controller do
let(:user) { create :user }
describe "After sing-in" do
it "redirects to the /yourpath/ home page" do
expect(subject.after_sign_in_path_for(user)).to eq(yourpath_root_path)
end
end
end
I found this answer through Google recently and thought I would add my solution. I didn't like the accepted answer because it was testing the return value of a method on the application controller vs testing the desired behavior of the app.
I ended up just testing the call to create a new sessions as a request spec.
RSpec.describe "Sessions", type: :request do
it "redirects to the internal home page" do
user = FactoryBot.create(:user, password: 'password 123', password_confirmation: 'password 123')
post user_session_path, params: {user: {email: user.email, password: 'password 123'}}
expect(response).to redirect_to(internal_home_index_path)
end
end
(Rails 5, Devise 4, RSpec 3)
context "without previous page" do
before do
Factory.create(:user, email: "junior#example.com", password: "123456", password_confirmation: "123456")
request.env["devise.mapping"] = Devise.mappings[:user]
post :create, user: { email: "junior#example.com", password: "123456" }
end
end
it { response.should redirect_to(root_path) }
context "with previous page" do
before do
Factory.create(:user, email: "junior#example.com", password: "123456", password_confirmation: "123456")
request.env["devise.mapping"] = Devise.mappings[:user]
request.env['HTTP_REFERER'] = 'http://test.com/restaurants'
post :create, user: { email: "junior#example.com", password: "123456" }
end
it { response.should redirect_to("http://test.com/restaurants") }
end