Ruby on rails - Devise - Integration Testing - ruby-on-rails

I am trying to test my sign up page but I don't understand why it doesn't work.
#SIGN UP FLOWS
test "signed up user with valid info is redirected to root" do
get root_path
post user_registration_path, :user_email => "asasas#as.com", :user_password => "asasas", :user_password_confirmation => "asasas"
follow_redirect!
assert_equal "/", path
end
I have also tried :
#SIGN UP FLOWS
test "signed up user with valid info is redirected to posts" do
get root_path
#david = User.create(email: "aaaasadasadaddamdaaa#aaa.com", password: Devise::Encryptor.digest(User, "aaaaaa"))
post user_registration_path, 'user[email]' => #david.email, 'user[password]' => #david.password, 'user[password_confirmation]' => #david.password
assert_equal "/", path
end
Instead of going to my root which is / It stays on the same page. I tried to do follow_redirect! but it gives me the error that reduction is not possible because RuntimeError: not a redirect! 200 OK.
What am I doing wrong? I have looked at devise GitHub page but there is no information about registration testing.

Related

How to log in/out users with Devise in Rails4 testing

I'm trying to ensure proper user access is maintained with Devise in Rails 4, and I'm having a hard time logging a user in in the test suite.
The simplest case:
require 'test_helper'
include Devise::TestHelpers
class SiteLayoutTest < ActionDispatch::IntegrationTest
def setup
#user = users(:test1)
end
test "logged in should get index" do
sign_in #user
get users_path
assert_response :success
assert_select "title", "User Index"
end
end
So far I've not done more really than just implement Devise and a Users controller with the appropriate actions.
I consistently get: NoMethodError: undefined method 'env' for nil:NilClass, referring specifically to the line containing sign_in #user and I can find other instances of people getting the same error, but never seem to find an actual solution to the problem I'm attempting to solve.
How do I log a user in with Devise in Rails 4 for testing purposes? Thanks.
EDIT:
fixtures/users.yml
test1:
id: '1'
email: 'test1#example.com'
encrypted_password: <%= Devise::Encryptor.digest(User, "password") %>
created_at: <%= Time.now - 6.minutes %>
updated_at: <%= Time.now - 4.minutes %>
SOLUTION IN SITU:
test "logged in should get index" do
post user_session_path, 'user[email]' => #user.email, 'user[password]' => 'password'
get users_path
assert_response :success
assert_select "title", "User Index"
end
This is from their docs:
"Do not use Devise::TestHelpers in integration tests."
You have to sign in manually. This is an example of a test for a website that does not allow users to get to the root path unless signed in. You can create a method in a support file that signs in the user manually and then call it whenever you want to sign in the user, so that you don't have to use this code every time you need to sign in a user.
require 'test_helper'
class UserFlowsTest < ActionDispatch::IntegrationTest
test "signed in user is redirected to root_path" do
get user_session_path
assert_equal 200, status
#david = User.create(email: "david#mail.com", password: Devise::Encryptor.digest(User, "helloworld"))
post user_session_path, 'user[email]' => #david.email, 'user[password]' => #david.password
follow_redirect!
assert_equal 200, status
assert_equal "/", path
end
test "user is redirected to sign in page when visiting home page" do
get "/"
assert_equal 302, status
follow_redirect!
assert_equal "/users/sign_in", path
assert_equal 200, status
end
end
EDIT: Just in case it's helpful in the future. You can use Warden Test Helpers for integration tests but the way above is a better test. This is a working example:
require 'test_helper'
include Warden::Test::Helpers
class UserFlowsTest < ActionDispatch::IntegrationTest
test "user can see home page after login" do
#david = User.create(email: "david#mail.com", password: Devise::Encryptor.digest(User, "helloworld"))
login_as(#david)
get "/"
assert_equal 200, status # User gets root_path because he loged in
assert_equal "/", path
logout
get "/"
assert_equal 302, status # User is redirected because he loged out
Warden.test_reset! #reset Warden after each example
end
end

How to "browse" a site with a minitest integration test

I am trying to write an integration test on rails which is supposed to visit my various pages but I can't get past the login.
require 'test_helper'
class UserSimulationTest < ActionDispatch::IntegrationTest
test "login site" do
# login via https
https!
get "users/sign_in"
assert_response :success
post_via_redirect "users/sign_in", username: users(:User_1).email
assert_equal "/users/sign_in", path
https?
assert_response :success
end
test "go to voting" do
https!
get "voting"
post_via_redirect "users/sign_in", username: users(:User_1).email
post_via_redirect "voting"
assert_equal "/voting", path
assert_response :success
end
end
Then i get this error because it redirects me to the login again.
Minitest::Assertion: Expected: "/voting"
Actual: "/users/sign_in"
test/integration/user_simulation_test.rb:23:in `block in <class:UserSimulationTest>'
Finished in 0.84105s
2 tests, 4 assertions, 1 failures, 0 errors, 0 skips
Process finished with exit code 0
I managed to solve my problem by passing the correct username and password parameters from my fixtures. I am using devise so i created a login method in test_helper.rb
def login(user)
post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.encrypted_password
end

Different '/' root path for users depending if they are authenticated (using devise)

I'm writing a rails app in which I have an Editor and a Publication model. I'm using devise for editors authentication and, since an editor can't do anything as guest, I wrote a custom layout to use for the login page and I want that a guest user can see only the login page.
Now I'm trying to achieve the following behavior in my app but unsuccessfully:
require 'spec_helper'
require 'capybara/rails'
describe "Authentication" do
describe "when logged in" do
before(:each) do
#editor = Factory(:editor, :password => 'secret')
visit '/'
fill_in 'Login', :with => #editor.login
fill_in 'Password', :with => 'secret'
click_button 'Sign in'
page.should have_content('Signed in successfully.')
end
it "getting / should render publication page with no redirection" do
visit '/'
page.should_not have_content('Login')
page.should have_content('Publications')
# assert that there is no redirection
page.current_path.should == '/'
end
it "visits the sign_in page should redirect to /" do
visit '/editors/sign_in'
page.should have_content('Publications')
page.current_path.should == '/'
end
end
describe "when not logged in" do
it "getting / should not display the sign in warning" do
visit '/'
# I want to get rid of this message
page.should_not have_content('You need to sign in or sign up before continuing.')
end
it "getting / should not redirect to the sign_in default page" do
visit '/'
page.should have_content('Login')
# assert that there is no redirection
page.current_path.should == '/'
end
it "getting the the sign_in default path works" do
visit '/editors/sign_in'
page.should have_content('Login')
page.current_path.should == '/editors/sign_in'
end
it "login works and redirect me to the publications page (with /)" do
#editor = Factory(:editor, :password => 'secret')
visit '/'
fill_in 'Login', :with => #editor.login
fill_in 'Password', :with => 'secret'
click_button 'Sign in'
page.should have_content('Signed in successfully.')
page.current_path.should == '/'
page.should have_content('Publications')
end
end
end
The main issue is that I want to get rid of 'You need to sign in or sign up before continuing.' message when a guest user visit '/'.
I tried with hints taken from here and here but with no luck.
Any hint on how implement this with devise?
Thanks.
I think you should use something like that
authenticated :user do
root :to => 'users#index', as: :authenticated_root
end
root :to => 'welcome#index'
since rails4 doesn't allow routes with same name you need to specify as:
This post explain how to achieve that:
authenticated :user do
root :to => "main#dashboard"
end
root :to => "main#index"
Here is the pull request which implement this with some technical details
authenticated :user do
root :to => 'home#index', :as => :authenticated_root
end
root :to => redirect('/users/sign_in')
Straight from the horse's mouth at the How to's for the devise gem. :D
Also because of you need to name these root paths in Rails 4 you might find that it is annoying when you want to just use root_path on say your logo to take you back to the dashboard or homepage respectively. I just created helpers in the ApplicationHelper that solves that easily.
module ApplicationHelper
def root_path
if user_signed_in?
authenticated_root_path
else
unauthenticated_root_path
end
end
def root_url
if user_signed_in?
authenticated_root_url
else
unauthenticated_root_url
end
end
end
Yeah, this issue was really frustrating for me as well.
Ultimately I ended up just hiding the flash message on the sessions#new page. Not a great solution at all since sometimes you /do/ want that message to show up..
I gave up after a while, but I wonder if you could use this approach, but set some flag inside that lambda and have a before_filter in your sessions controller that empties flash if it's present. Something like...
#routes
=> 'users#dashboard', :constraints => lambda {|r| r.env["skip_flash"] = true; r.env["warden"].authenticate? }
#sessions_controller.rb
before_filter :only=>[:new] do
flash[:notice] = nil if request.env["skip_flash"]
request.env["skip_flash"] = false
end
I'm not too familiar with how the routes constraint and the request object work.. but just an idea. This would turn off the flash message only when you're trying to access the "/" and not other pages..
I hope somebody has a nice solution!

Rails/RSpec: reset_session not changing Set-Cookie HTTP header value during integration tests

I'm writing an integration test to make sure my webapp isn't vulnerable to session fixation.
I have manually verified that reset_session is actually firing in the authentication logic, and further that the cookie does indeed change when I log in with my web browser (so, I'm not vulnerable to session fixation anymore), but I can't get my RSpec integration test to successfully verify this.
Here is my RSpec integration test.
require 'spec_helper'
describe "security" do
self.use_transactional_fixtures = false
append_after(:each) do
ALL_MODELS.each &:delete_all
end
describe "session fixation" do
it "should change the cookie session id after logging in" do
u = test_user :active_user => true,
:username => "nobody#example.com",
:password => "asdfasdf"
u.save!
https!
get_via_redirect "/login"
assert_response :success
cookie = response.header["Set-Cookie"].split(";").select{|x| x.match(/_session/)}[0].split("=")[1].strip
post_via_redirect "/login", "user[email]" => "nobody#example.com",
"user[password]" => "asdfasdf",
"user[remember_me]" => "1"
assert_response :success
path.should eql("/dashboard")
cookie.should_not eql(response.header["Set-Cookie"].split(";").select{|x| x.match(/_session/)}[0].split("=")[1].strip)
end
end
end
Everything works except for the very last assert. The cookie doesn't change.
Are there any known issues with RSpec/Rails integration tests where reset_session doesn't work as expected? What can I do to write a test that verifies session fixation is not an issue?
So I eventually did end up figuring this out.
I was trying to edit the response header directly to test cookies, but I guess that's not the blessed way.
In integration tests with Rails 2.x anyway, there's a cookies hash that you can use. Here's what the test ended up looking like:
u = test_user :active_user => true,
:username => "nobody#example.com",
:password => "asdfasdf"
u.save!
https!
get_via_redirect "/login"
assert_response :success
cookie = cookies['_session']
cookie.should be_present
path.should == "/login"
post_via_redirect "/login", "user[email]" => "nobody#example.com",
"user[password]" => "asdfasdf",
"user[remember_me]" => "1"
assert_response :success
path.should eql("/?login_success=1")
new_cookie = cookies['_session']
new_cookie.should be_present
cookie.should_not eql(new_cookie)

Controller test fails for rails user authentication

--preface: ignore if you want.
I'm new to rails, and working on a project that will require user authentication.
I found this tutorial and have been trying to go through it and understand what's happening. Of course, it's not exactly what I need as-is, so I've been modifying as I go along. The tutorial is also out of date in some areas, so of course I've had to update my code. So part of my problem is that I'm not sure if the bug is in my modifications, or some function that's been deprecated, or what.
--the question
This is the (simplest) test that fails. (" expected to not be nil" on the first assert statement.)
def test_authentication
#check we can log in
post :login, :user => { :username => "bob", :password => "test" }
assert_not_nil session[:user_id]
assert_equal users(:bob).id, session[:user_id]
assert_response :redirect
assert_redirected_to :action => 'welcome'
end
It calls the user_controller action login:
def login
if request.post?
if session[:user_id] = User.authenticate(params[:user][:username], params[:user][:password])
flash[:message] = "Login succeeded!"
redirect_to_stored
else
flash[:warning] = "Login failed."
end
end
end
which calls the User method authenticate. I know that authenticate works properly, however, because I have a single test that does pass:
def test_registration
#check that we can register and are logged in automatically
post :register, :user => { :username => "newuser", :password => "pass", :password_confirmation => "pass", :email => "newuser#web.com" }
assert_response :redirect
assert_not_nil session[:user_id]
assert_redirected_to :action => 'welcome'
end
which calls the user_controller action register
def register
#user = User.new(params[:user])
if request.post?
if #user.save
session[:user_id] = User.authenticate(#user.username, #user.password)
flash[:message] = "Registration succeeded"
redirect_to :action => 'welcome'
end
else
flash[:warning] = "Registration failed"
end
end
which successfully calls authenticate.
the users fixture has one relevant record:
bob:
username: bob
email: bob#mcbob.com
hashed_password: 77a0d943cdbace52716a9ef9fae12e45e2788d39 # test
salt: 1000
I've tested the hashed password and salt - "test" is the correct password.
So by my analysis, the bug has got to be in one of 3 places:
how I'm sending my post request,
how I'm accessing the parameters in the login action,
or some aspect of the fixture loading.
(originally I was using the tutorial's code to load the fixture explicitly (self.use_instantiated_fixtures = true; fixtures :users), but I read that all fixtures are automatically loaded before testing, so I took it out. That didn't change a thing.)
Of course, since I can't seem to find the problem in those areas, it could just as well be anywhere else.
Is it possible that there's a filter that is preventing your action getting called? If there's a general :before_filter => 'login_required' then you might not be reaching your login functionality at all. (Though admittedly the register action would have to be excluded for that test to pass)
In cases like this it's useful to stick some logging in (or run through a debugger) to see whether you even get to the part of the method that you think is failing. If it were me I'd stick a logger.debug("...") as the first line of the login method and then another after the check for request.post? and then another after the authentication check.

Resources