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
Related
I have two tests. The results of each depend upon the user count.
At the moment, I have no user fixtures. The second test introduces a user in its setup method. The first test would fail if there were users in the db.
But I want to introduce users in users.yml. As such, the first test will fail because of the existing users. Is there any way I can instruct this test to ignore fixtures/users.yml?
require 'test_helper'
class UsersSignupTestWithoutExistingUsers < ActionDispatch::IntegrationTest
test "Signup page is accessible" do
get new_user_registration_path
assert_response :success
end
end
class UsersSignupTestWithExistingUsers < ActionDispatch::IntegrationTest
def setup
post user_registration_path, params: {user: {
email: "user#test.com",
password: "password",
password_confirmation: "password"
}}
end
test "Signup page will redirect" do
get new_user_registration_path
assert_response :redirect
end
end
In the test that requires no users in the DB you can stub the method that looks in the DB for users so that it returns an empty set for that test.
I have a problem with unit testing in ruby on rails (rails v. 5.001). I use devise and cancancan for authorization. The user needs to login in a test unit, but how can I implement this without redirecting to http://www.example.com/users/sign_in? Here is the code:
appointments_controller_test.rb
require 'test_helper'
class AppointmentsControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
def sign_in(user)
post user_session_path \
"heiko#me.com" => user.email,
"hhdk#s0" => user.password
end
setup do
#heikoAppointment = appointments(:appointment_heiko)
#heiko = users(:user_heiko)
sign_in(#heiko)
end
test "should get index" do
get appointments_url
assert_response :success
end
test "should get new" do
sign_in(#heiko)
get new_appointment_url
assert_response :success
end
And here is the Error I get when I run the test:
Error:
AppointmentsControllerTest#test_should_get_new [C:/Users/Clemens/meindorfladen/Server/test/controllers/appointments_controller_test.rb:33]:
Expected response to be a <2XX: success>, but was a <302: Found> redirect to <http://www.example.com/users/sign_in>
The issue is your user is not confirmed.
You need to pass in
confirmed_at = Time.now
to the user you create. That will then stop you getting redirected.
I recently installed Devise in my app to handle auth, replacing the auth system from Michael Hartl's tutorial. It's working fine in the app itself, but I can't get my tests to auth properly, so they're pretty much all failing, which makes me sad. I'm using Rails 4 with Minitest.
Here's an example of one of my controller tests that fails:
learning_resources_controller_test.rb
require 'test_helper'
class LearningResourcesControllerTest < ActionController::TestCase
def setup
#user = users(:testuser1)
end
test "user can submit new resource" do
sign_in #user # Devise helper
post :create, {:learning_resource => {:name => "My resource"}}
resource = assigns(:learning_resource)
assert_redirected_to topic_path(#topic1, :learning_resource_created => "true")
end
end
test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
fixtures :all
# Return true is test user is signed in
def is_signed_in?
!session[:user_id].nil?
end
def sign_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
if integration_test?
# Sign in by posting to the sessions path
post signin_path, session: { email: user.email,
password: password,
remember_me: remember_me }
else
# Sign in using the session
session[:user_id] = user.id
end
end
private
def integration_test?
defined?(post_via_redirect)
end
end
class ActionController::TestCase
include Devise::TestHelpers
end
fixtures/users.yml
testuser1:
name: Test User 1
email: testuser1#mydumbdomain.com
password_digest: <%= User.digest('password') %>
The assert_redirected_to in the test always fails as the redirect is the sign in page instead of the topic page. All of my other tests fail in similar ways, indicating the user isn't signed in. I have scoured the Devise wiki and docs, but most of them cover testing with Rspec, not Minitest.
I tried using byebug within the test after the sign_in to check out the session, and I get this:
(byebug) session.inspect
{"warden.user.user.key"=>[[336453508], ""]}
If I try to call the :create, I get this error:
DEPRECATION WARNING: ActionDispatch::Response#to_ary no longer
performs implicit conversion to an array. Please use response.to_a
instead, or a splat like status, headers, body = *response. (called
from puts at
/Users/me/.rbenv/versions/2.2.2/lib/ruby/2.2.0/forwardable.rb:183)
302 {"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"1;
mode=block", "X-Content-Type-Options"=>"nosniff",
"Location"=>"http://test.host/signup",
"Set-Cookie"=>"request_method=POST; path=/",
"Content-Type"=>"text/html; charset=utf-8"}
Any ideas what I'm missing here?
The error is with hash
post :create, {:learning_resource => {:name => "My resource"}}
Try
post :create, :learning_resource => {:name => "My resource"}
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
I'm trying to confirm whether devise is using login, not email for sign in. That's what I have so far:
test/controllers/sessions_controller_test.rb:
require 'test_helper'
class Devise::SessionsControllerTest < ActionController::TestCase
test "should sign in using login" do
#request.env["devise.mapping"] = Devise.mappings[:user]
post :create, user: {login: 'login', password: 'password'}
assert_response :success
end
end
And now it says:
1) Error:
Devise::SessionsControllerTest#test_should_sign_in_using_login:
NoMethodError: undefined method `authenticate?' for nil:NilClass
test/controllers/sessions_controller_test.rb:7:in `block in <class:SessionsControllerTest>'
Why doesn't it work? What am I doing wrong?
The error disappeared after I added include Devise::TestHelpers in test/test_helper.rb as documentation suggests:
class ActiveSupport::TestCase
...
include Devise::TestHelpers
end
And now my tests look this way:
test/controllers/sessions_controller_test.rb:
require 'test_helper'
class Devise::SessionsControllerTest < ActionController::TestCase
test "should have login field" do
#request.env["devise.mapping"] = Devise.mappings[:user]
get :new
assert_select '[name="user[login]"]'
end
test "should sign in using login" do
#request.env["devise.mapping"] = Devise.mappings[:user]
post :create, user: {login: 'login', password: 'password'}
assert_redirected_to root_url
end
end
test/fixtures/users.yml (how to put password in a fixture):
one:
login: login
encrypted_password: <%= Devise.bcrypt(User, 'password') %>