Rails 5.1.6
Ruby 2.5.0
I am trying to run a simple test for a redirect in one of my controllers using Shoulda Matcher gem (following the documentation) and minitest:
home_controller.rb:
class HomeController < ApplicationController
def index
#redirect on login
if user_signed_in?
redirect_to controller: 'home', action: "dashboard_#{current_user.user_role}"
end
end
test/controllers/home_controller_test.rb:
class HomeControllerTest < ActionController::TestCase
context 'GET #index' do
setup { get :index }
should redirect_to(action: "dashboard_#{current_user.user_role}")
end
end
Error:
Undefined method current_user for homecontrollertest
I'm using Devise and was wondering if anyone could assist to get my test to work? I can provide more info if required.
EDIT:
Tried this:
home_controller_test.rb
require 'test_helper'
class HomeControllerTest < ActionController::TestCase
include Devise::Test::ControllerHelpers
context 'GET #index' do
user = users(:one)
sign_in user
get :index
should redirect_to(action: "dashboard_#{user.user_role}")
end
end
users.yml
one:
name: 'John'
email: 'some#user.com'
encrypted_password: <%= Devise::Encryptor.digest(User, 'password') %>
user_role: 1
Gemfile
gem 'shoulda', '~> 3.5'
gem 'shoulda-matchers', '~> 2.0'
Test_helper.rb
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
Get undefined method users error.
NoMethodError: undefined method `users' for HomeControllerTest:Class
/mnt/c/code/studytaps/test/controllers/home_controller_test.rb:9:in `block in <class:HomeControllerTest>'
/mnt/c/code/studytaps/test/controllers/home_controller_test.rb:8:in `<class:HomeControllerTest>'
/mnt/c/code/studytaps/test/controllers/home_controller_test.rb:4:in `<top (required)>'
Tasks: TOP => test
(See full trace by running task with --trace)
You need to include devise test helper to your test
class HomeControllerTest < ActionController::TestCase
include Devise::Test::ControllerHelpers
context 'GET #index' do
user = users(:one) # if you use fixtures
user = create :user # if you use FactoryBot
sign_in user
get :index
should redirect_to(action: "dashboard_#{user.user_role}")
end
end
Related
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! :)
I'm using Rails 5, and Devise 3.5.1.
Going through a nice (older) book about creating/testing an API, which uses Devise authentication. It was written before Rails 5, so I chose not to use the new api-only version.
Here's my test...
#/spec/controllers/api/v1/users_controller_spec.rb
require 'rails_helper'
describe Api::V1::UsersController, :type => :controller do
before(:each) { request.headers['Accept'] = "application/vnd.marketplace.v1" }
describe "GET #show" do
before(:each) do
#user = FactoryGirl.create :user
get :show, params: {id: #user.id}, format: :json
end
it "returns the information about a reporter on a hash" do
user_response = JSON.parse(response.body, symbolize_names: true)
expect(user_response[:email]).to eql #user.email
end
it { should respond_with 200 }
end
end
And here's a completely unexpected RSpec error
Devise::MissingWarden:
Devise could not find the `Warden::Proxy` instance on your request environment.
Make sure that your application is loading Devise and Warden as expected and that the `Warden::Manager` middleware is present in your middleware stack.
If you are seeing this on one of your tests, ensure that your tests are either executing the Rails middleware stack or that your tests are using the `Devise::Test::ControllerHelpers` module to inject the `request.env['warden']` object for you.
So I go here - http://www.rubydoc.info/gems/devise/Devise/Test/ControllerHelpers
and tried this -> include Devise::Test::ControllerHelpers
which didn't help because the file controller_helpers.rb is nowhere in my project
What did I miss here?
Thanks
You could add the following to your rails_helper:
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
end
This will include Devise::Test::ControllerHelpers module in all :controller specs.
In the spec_helper.rb add:
config.include Devise::Test::ControllerHelpers, :type => :controller
MiniTest, Rails 4
This works for vanilla Rails 4 MiniTest
test\controllers\users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
include Warden::Test::Helpers
include Devise::Test::ControllerHelpers
setup do
# https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
# #user = users(:admin)
# sign_in #user
end
teardown do
Warden.test_reset!
end
test "login as admin" do
#user = users :admin
sign_in #user
get :dashboard
assert_redirected_to admin_dashboard_path
end
end
I'm newbie to Rails Testing.
After following some tutorial online, I could able to setup and run testing for Model.
But when trying to test for Controller, Testing was failed as it is redirected to login page.
I've tried every instruction I can find on web to sign in for devise and still couldn't able to sign in and move forward.
Appreciate if someone could help and give me a direction to move forward.
AwardedBidsControllerTest
test_should_get_index FAIL (0.45s)
MiniTest::Assertion: Expected response to be a <:success>, but was <302>
Below is my setup
test/test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/rails"
require "minitest/reporters"
Minitest::Reporters.use!(
Minitest::Reporters::SpecReporter.new,
ENV,
Minitest.backtrace_filter
)
class ActiveSupport::TestCase
fixtures :all
include FactoryGirl::Syntax::Methods
end
class ActionController::TestCase
include Devise::TestHelpers
end
test/controllers/awarded_bids_controller_test.rb
require "test_helper"
class AwardedBidsControllerTest < ActionController::TestCase
test "should get index" do
user = create(:user)
sign_in user
get :index
assert_response :success
end
end
app/controllers/awarded_bids_controller.rb
class AwardedBidsController < ApplicationController
before_filter :authenticate_user!
def index
#awarded_bids = AwardedBid.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #awarded_bids }
end
end
end
test/factories/users.rb
#using :login instead of :email.
FactoryGirl.define do
factory :user do |u|
u.login "user"
u.name "Normal"
u.surname "User"
u.password "Abc2011"
end
end
Below is the version info.,
JRuby 1.7.21(Ruby 1.9.3)
Rails 3.2.22
Devise 3.0.4
Minitest 4.7.5
From integration_helpers.rb, for example:
class PostsTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
test 'authenticated users can see posts' do
sign_in users(:bob)
get '/posts'
assert_response :success
end
end
In your test_helper.rb file's ActiveSupport::TestCase class, add a new method log_in_as like this:
require "test_helper"
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/rails"
require "minitest/reporters"
Minitest::Reporters.use!(
Minitest::Reporters::SpecReporter.new,
ENV,
Minitest.backtrace_filter
)
class ActiveSupport::TestCase
fixtures :all
include FactoryGirl::Syntax::Methods
# Returns true if a test user is logged in.
def is_logged_in?
!session[:user_id].nil?
end
# Logs in a test user.
def log_in_as(user, options = {})
password = options[:password] || 'password'
remember_me = options[:remember_me] || '1'
if integration_test?
post login_path, session: { email: user.email,
password: password,
remember_me: remember_me }
else
session[:user_id] = user.id
end
end
private
# Returns true inside an integration test.
def integration_test?
defined?(post_via_redirect)
end
end
class ActionController::TestCase
include Devise::TestHelpers
end
Then, use this log_in_as method instead of sign_in in your test:
require "test_helper"
class AwardedBidsControllerTest < ActionController::TestCase
test "should get index" do
user = create(:user)
log_in_as user
get :index
assert_response :success
end
end
I am learning Rails and very new to testing but so far I've managed to build something with minimal errors. However, the issue I am running into is that my tests are complaining for methods that cannot be found and no route matching.
To my understanding tests should be run frequently based Hartl's - Railstutorial 3.3. Many StackO threads and online articles seem to pertain to utilizing test suites I don't use like RSpec, etc...so the test configurations are confusing. My testing suite is set up similiar to Hartl's - Railstutorial 3.3 and below are the gems loaded for testing.
gem 'better_errors', '~> 2.1.1'
gem 'binding_of_caller'
gem 'minitest-reporters', '1.0.5'
gem 'mini_backtrace', '0.1.3'
gem 'guard-minitest', '2.3.1'
gem 'ruby-prof'
NoMethodError: undefined method
Error: (_I have two error similar errors to below_)
ServicesControllerTest#test_should_get_index:
NoMethodError: undefined method 'services' for nil:NilClass
app/controllers/services_controller.rb:6:in 'index'
test/controllers/services_controller_test.rb:9:in 'block in <class:ServicesControllerTest>"
Services Controller
def index
#services = current_tech.services
end
services_controller_test.rb
require 'test_helper'
class ServicesControllerTest < ActionController::TestCase
setup do
#service = services(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:services)
end
end
I believe the reason I am receiving this error is because of Devise.
If I were to setup the following index action below, the test will pass.
def index
#services = Tech.first.services
end
How do I correct this so that this test passes?
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"tech"}
Error:
CarsControllerTest#test_should_get_show:
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"cars"}
test/controllers/cars_controller_test.rb:5:in `block in <class:CarsControllerTest>
Rake routes pertaining to cars
tech_cars POST - /techs/:tech_id/cars(.:format) - cars#create
car GET - /cars/:id(.:format) - cars#show
Routes
Rails.application.routes.draw do
devise_for :customers, controllers: { sessions: 'customers/sessions' }
devise_for :techs, controllers: { sessions: 'techs/sessions' }
resources :techs, :only => [:index, :show], shallow: true do
resources :cars, only: [:show, :create]
end
resources :services, :garages
root "home#index"
end
cars_controller.rb
class CarsController < ApplicationController
before_action :set_car
def show
#garage = Garage.find(params[:id])
#tech = #tech.service.car.id
end
def create
#garage = Garage.create(tech_first_name: #car.service.tech.first_name,
customer_id: current_customer.id,
customer_street_address: current_customer.street_address,
customer_city: current_customer.city,
customer_state: current_customer.state,
customer_zip_code: current_customer.zip_cod)
if #garage.save
redirect_to techs_path, notice: "Working"
else
redirect_to techs_path, notice: "Uh oh, flat tire"
end
end
private
def set_car
#car = Car.find(params[:id])
end
def car_params
params.permit(:service_name, :garage_photo)
end
end
cars_controller_test.rb
require 'test_helper'
class CarControllerTest < ActionController::TestCase
test "should get show" do
get :show
assert_response :success
end
end
cars.html.erb (only links on page)
<%= button_to 'View Garage', tech_cars_path(tech_id: #car.service.tech.id, id: #car) %>
<%= link_to 'Back to tech', tech_path(#car.service.tech.id) %>
As you may gather, I am building a Garage not a Car object within the Cars controller. Would this be a problem for tests? My application functions fine as is. On a side note, I am also having difficulty trying to associate car_params strong params for instance variables but that's another StackO post.
*test/test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require "minitest/reporters"
Minitest::Reporters.use!
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
class ActionController::TestCase
include Devise::TestHelpers
end
Is there something I am missing or haven't configured correctly?
Please know my application appears to be working fine, it's just I would like to suppress these tests errors.
Please advise on how to get these test errors to pass.
Thanks
First issue: Your feeling is correct. Devise comes with Devise::TestHelpers which you've mixed into ActionController::TestCase in your test helper file. One of the methods it provides is sign_in which lets you spoof a logged in user as part of your test. Assuming you've got a model called Tech and you're following standard Rails conventions, you'll need to add something like:
sign_in techs(:some_tech)
to either your setup block or directly into your test body before the call to get :index. That will ensure that current_tech returns something non-nil and remove the immediate NoMethodError.
Second issue: Your :show action expects to receive the ID of a known Car as part of the URL. Replace your current invocation of the controller with:
require 'test_helper'
class CarControllerTest < ActionController::TestCase
setup do
#car = cars(:some_car)
end
test "should get show" do
get :show, id: #car.id
assert_response :success
end
end
I am trying to move a helper method from a controller test to the test_helper.rb:
# example_controller_test.rb
require 'test_helper'
class ExampleControllerTest < ActionController::TestCase
should 'get index' do
turn_off_authorization
get :show
assert_response :success
end
end
# test_helper.rb
class ActionController::TestCase
def turn_off_authorization
ApplicationController.any_instance
.expects(:authorize_action!)
.returns(true)
end
end
However, I'm getting an error:
NameError: undefined local variable or method `turn_off_authorization' for #<ExampleControllerTest:0x000000067d6080>
What am I doing wrong?
Turns out that I had to wrap the helper method into a module:
# test_helper.rb
class ActionController::TestCase
module CheatWithAuth do
def turn_off_authorization
# some code goes here
end
end
include CheatWithAuth
end
I still don't know why the original version didn't work.
The idea came from another answer:
How do I write a helper for an integration test in Rails?
Edit: Another solution just came from my friend:
# test_helper.rb
class ActiveSupport::TestCase
def turn_off_authorization
# some code goes here
end
end
Note that ActiveSupport::TestCase (not ActionController::TestCase) is being used here.