Rails 6 Devise test helpers without RSpec - ruby-on-rails

Devise test helpers with Rails 6 without Rspec doesn't seem to work. Here is the code, any idea why it might be getting errors?
Controller:
class VehiclesController < ApplicationController
before_action :authenticate_user!, only: [:new]
def index
#vehicles = Vehicle.all
end
def new
#vehicle = Vehicle.new
end
end
test/test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require_relative "../config/environment"
require "rails/test_help"
class ActiveSupport::TestCase
# Run tests in parallel with specified workers
parallelize(workers: :number_of_processors)
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
include Devise::Test::IntegrationHelpers
end
user fixture:
valid_user:
first_name: "Toseef"
last_name: "zafar"
email: "exampleuser#gmail.com"
encrypted_password: <%= Devise::Encryptor.digest(User, '12345678') %>
controller test:
require "test_helper"
class VehiclesControllerTest < ActionDispatch::IntegrationTest
test "should be able to get to new form page" do
sign_in users(:valid_user)
get new_vehicles_url
assert_response :success
end
end
and this is the error I get:
Failure:
VehiclesControllerTest#test_should_be_able_to_get_to_new_form_page [/test/controllers/vehicles_controller_test.rb:12]:
Expected response to be a <2XX: success>, but was a <302: Found> redirect to <http://www.example.com/users/sign_in>
Response body: <html><body>You are being redirected.</body></html>
rails test test/controllers/vehicles_controller_test.rb:9
Also, I don't know why it would point to http://www.example.com

The devise user model has got confirmable hence when we do sign_in users(:valid_user) devise creates a user but because the user is not confirmed (i.e. no confirmation email link clicking is involved) when we go to a secured URL it takes us back to login because user hasn't confirmed through clicking on the link from the email.
The solution to this is to set confirmed_at value to Time.now before sign_in
e.g.
#user = users(:valid_user)
#user.confirmed_at = Time.now
sign_in #user
after doing that the tests passed! :)

Related

setting up minitest with Devise with refactoring test_helper

Rails 7 application using devise for authentication.
fixture (model tests pass for all classes):
one:
email: 'me#mail.co'
encrypted_password: <%= User.new.send(:password_digest, '12345678')
test_helper which attempts to re-factor the login and action process into short one-liners (test_access) in the actual controller tests
require 'simplecov'
SimpleCov.start 'rails'
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
require 'webmock/minitest'
require 'barby/outputter/png_outputter'
require 'barby/barcode/ean_13'
class ActiveSupport::TestCase
fixtures :all
include Devise::Test::IntegrationHelpers
include Warden::Test::Helpers
parallelize(workers: :number_of_processors)
def log_in(user)
if integration_test?
login_as(user, :scope => :user)
else
sign_in(user)
end
end
class Minitest::Test
def setup
#one = users(:one)
end
end
private
def test_access(user, action)
get '/users/sign_in'
puts user.inspect
sign_in user
# puts user_signed_in?
# puts current_user.manager?
puts user.manager?
post user_session_url
follow_redirect!
puts action
get action
assert_response :success
end
and the controller_test (presently on an intermediate refactoring level, as the method should extend to an array of users)
require "test_helper"
class LokationsControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
setup do
#users_allowed = [ #one ]
#actions = [dummy_lokations_url]
end
test "should get dummy" do
#actions.each do |action|
test_access(#one, action)
assert_response :success
end
end
the problem is there seems to be no way of verifying whether the user is signed in or not, notwithstanding the inclusion of devise integration helpers. The output in the console points to proper values, but the result is the opposite of the method that should be applied
#<User id: 760812109, email: [...]>
true
http://www.example.com/lokations/dummy
[...]
Expected response to be a <2XX: success>, but was a <302: Found> redirect to <http://www.example.com/>
Response body: <html><body>You are being redirected.</body></html>
because the before_action method applied on the action specifies
def index_manager
if user_signed_in? && current_user.manager?
else
redirect_to root_path and return
end
end
Where is the above mistaken ? (I have a nagging doubt that something may be missing as this pattern has been successfully followed in the past)
Sorry but this is just not a good/acceptable refactor.
Do not set the password digest - ever. That should only be known by the underlying system that encrypts the password. Your code and tests should not even know that it exists. Your code should only ever set the password attribute with a cleartext.
These are integration and not controller tests. Controller tests are subclasses of ActionController::TestCase used in legacy applications. They should not be used. While this might seem like nickpicking you're only confusing yourself and others by conflating them.
Avoid monkeypatching a bunch of junk into ActiveSupport::TestCase or even worse Minitest::Test. If you MUST monkeypatch then write your methods in a module and include it so that the stack trace points there. But do it at the correct level in the class heirarchy. For example separate between the helpers for integration and system tests and include them into ActionDispatch::IntegrationTest and ActionDispatch::SystemTest instead of polluting all your tests.
If you need to add a lot of additional behavior don't reopen the class. Instead create your own test class which inherits from ActionDispatch::IntegrationTest and have your tests subclass it.
If you have code that you want to reuse in multiple tests like the test setup use modules that can be included where they are actually needed.
test_access doesn't belong in the shared subclass way up the tree. This belongs in an actual integration test which covers your authentication system flows or that a specific resource is authorized correctly.
Devise is an authentication system (who is the user?) - but what you're doing here is authorization (who can do what?). Devise doesn't provide any kind of authorization besides preventing access for users that are not authenticated. Neither should it (see SRP).
The index_manager method isn't a good way to implement authorization. If you want to reinvent the wheel make sure you raise an exception and use rescue_from so that the callback chain is halted and so that you're not repeating yourself. You may want to look into existing systems like CanCanCan and Pundit.
What you actually want here is something more like:
class AuthorizationError < StandardError
end
class ApplicationController
before_action :authenticate_user! # use an opt-out secure by default setup.
rescue_from AuthorizationError, with: :deny_access
private
def authorize_manager!
unless current_user.manager?
raise AuthorizationError.new("You need to be a manager to access this resource")
end
end
def deny_access(exception)
redirect_to root_path, error: exception.message
end
end
module AuthorizationIntegrationHelpers
def assert_denies_access
follow_redirect!
assert_current_path root_path
# ...
end
end
class FoosFlowTest < ActionDispatch::IntegrationTest
include AuthorizationIntegrationHelpers
include Warden::Test::Helpers
test "should not be accessable to non-managers" do
login_as(users(:one))
get '/foos'
assert_denies_access
end
end

How to test after_sign_in_path_for by minitest

I changed the default behavior of after_sign_in_path_for method like this:
class ApplicationController < ActionController::Base
private
def after_sign_in_path_for(resource)
return admin_root_path if resource.is_a?(AdminUser)
request.referrer || root_path
end
end
It works find and now I want to test it by minitest.
But I couldn't figure out how to write integration test for it.
Although there is an answer for rspec, I couln't rewrite for minitest.
How to test after_sign_in_path_for(resource)?
How can I write the test for after_sign_in_path_for by minitest?
Rails: 5.1
devise: 4.5.0
require 'test_helper'
class ApplicationControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
setup do
2.times{ create(:post) }
#user = create(:user)
#admin_user = create(:admin_user)
end
test "should redirect to '/posts/1' after login" do
# get "/posts/1"
# sign_in(#user)
# test return back "/posts/1"
end
test "should redirect to '/posts/2' after login" do
# get "/posts/2"
# sign_in(#user)
# test return back "/posts/2"
end
test "should redirect to admin root page after login" do
# sign_in(#adminuser)
# test go to admin root page
end
end
You can test them like so:
require 'test_helper'
class ApplicationControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
setup do
#user = create(:user)
#admin_user = create(:admin_user)
end
test "should redirect to current page after login" do
sign_in(#user)
get :index
assert_redirected_to controller: "home", action: "index"
end
test "should redirect to admin root page after login" do
sign_in(#adminuser)
get :index
assert_redirected_to controller: "admin", action: "index"
end
end
assert_redirected_to API docs

How do I log out a user in a Rails integration/controller unit test?

I have the following test to ensure sign in is required. Most of the tests require a signed in user, so I have the user sign in on setup. However for this test, I need them signed out.
require 'test_helper'
class DealsControllerTest < ActionDispatch::IntegrationTest
include Warden::Test::Helpers
setup do
#deal = deals(:one)
#user = users(:one)
# https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
#user.confirmed_at = Time.now
#user.save
login_as(#user, :scope => :user)
end
teardown do
Warden.test_reset!
end
test "require sign in for deal list" do
logout #user
get deals_url
assert_redirected_to new_user_session_path
end
I get the error
Failure:
DealsControllerTest#test_require_sign_in_for_deal_list [C:/Users/Chloe/workspace/fortuneempire/test/controllers/deals_controller_test.rb:35]:
Expected response to be a <3XX: redirect>, but was a <200: OK>
It says it will work, but it's just not working.
https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
If for some reason you need to log out a logged in test user, you can use Warden's logout helper.
logout(:user)
Rails 5.0.2
Devise 4.2.1
logout by itself without any parameters worked. Maybe logout :user would work too. I wasn't using FactoryGirl so was confusing it for a FactoryGirl symbol.

Can't sign in when running controller tests with Devise / Rails 4 / Minitest

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"}

Simple example of Rails Testing Devise Controllers

I want the simplest example to test the validity of attributes of devise's actions for new registrations (email, password, password confirmation), sign_in and forgot_password. The internet is full of RSpec ones but I want to go native with what Rails 4.2 gives me and there is absolutely nothing.
I am stuck to the default implementation:
require 'test_helper'
class RegistrationsControllerTest < ActionController::TestCase
def setup
#controller = RegistrationsController.new
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Registrations.new(username: "John", email: "myemail#email.com")
end
end
I know this isn't too much but I am making my first steps in TDD, so please don't shoot !
How do I check for the validity of a user's attributes, for example a nil email or a password of 100 characters ?
You can check with rails in-build minitest
I have assumed you have overriding devise's registration_controller.
In tdd for controller you can write test cases for action.
Check modified code :-
require 'test_helper'
class RegistrationsControllerTest < ActionController::TestCase
include Devise::TestHelpers # for including devise's actions
def setup # this set up default settings for controller
#controller = RegistrationsController.new
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Registrations.new(username: "John", email: "myemail#email.com")
end
setup do # this used for setting global variable used in test file
#user= users(:one) # users is the fixture file of test in which you can set default data for test environment.
end
test "should create user" do # then you test cases for controller
sign_in users(:one)
post :create, users:{email:'test#test.com, password:'XXXX'...}# you can pass arguments for create method. Please check it once, i am not sure about names
assert_response :success
end
end
Rails minitest provides some assertion.

Resources