I am getting a uninitialized constant ControllerMacros (NameError), perhaps similar to these issues (1, 2, 3). I must be screwing up the syntax while trying to include controller macros so I can login with devise and pass controller tests in rspec. Link to GitHub repo.
Rails 4.1.8 and Ruby 2.1.2
spec/controllers/static_pages_controller_spec.rb
require 'rails_helper'
describe StaticPagesController, :type => :controller do
describe "GET #index" do
it "responds successfully with an HTTP 200 status code" do
login_user
get :index
expect(response).to be_success
expect(response).to have_http_status(200)
end
it "renders the index template" do
login_user
get :root
expect(response).to render_template("index")
end
end
end
spec/support/controller_macros.rb
module ControllerMacros
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:admin]
admin = FactoryGirl.create(:admin)
sign_in :user, admin # sign_in(scope, resource)
end
end
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the "confirmable" module
sign_in user
end
end
end
added lines to spec/rails_helper
#helps avoid authentication error during rspec:
config.include Devise::TestHelpers, :type => :controller
config.include ControllerMacros, :type => :controller
This worked for me.
spec/support/devise.rb
require 'devise'
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
Also make sure this line is uncommented in rails_helper.rb
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
I looks like you may need to add require 'support/controller_macros' to the top of your rails_helper.rb file. This directory would not be included by default with RSpec.
Related
I'm having an issue logging in a user with Devise/Rspec to hit a route for testing. I'm used the support/controller_macros module as outlined by devise, but whenever I try using
login_user in any part of my test I get the error: before is not available from within an example (e.g. an it block) or from constructs that run in the scope of an example (e.g. before, let, etc). It is only available on an example group (e.g. a describe or context block).
I've tried a moving things around, making sure all of my requires are set up correctly, etc.
My test:
require "rails_helper"
RSpec.describe BallotsController, type: :controller do
describe "index" do
it "renders" do
login_user
ballots_path
expect(response).to be_success
expect(response).to render_template("index")
end
end
end
(I've tried adding login_user inside the describe block, and the upper block as well)
My controller_macros:
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user_confirmed]
user = FactoryBot.create(:user_confirmed)
sign_in user
end
end
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:admin]
user = FactoryBot.create(:admin)
sign_in user
end
end
end
My spec helper:
require "rails_helper"
require_relative "support/controller_macros"
RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
config.include ControllerMacros, :type => :controller
config.include Devise::Test::ControllerHelpers, :type => :controller
config.example_status_persistence_file_path = "spec/examples.txt"
config.disable_monkey_patching!
if config.files_to_run.one?
config.default_formatter = "doc"
end
config.profile_examples = 10
config.order = :random
Kernel.srand config.seed
end
I expect it to log a user in, and for the controller to correctly hit the index route. Thank you!
Only issue in the code(to resolve the error, without digging into the debate of if this method should be used or not) is that you have login user within the it block, which can't be because it is calling before(:each).
If you see the device documentation, you will also see that it does not have this call in it block, but rather outside of it block in a describe block. Which applies this call to all it blocks in that describe block.
Your code willcbecome:
RSpec.describe BallotsController, type: :controller do
describe "index" do
login_user
it "renders" do
ballots_path
expect(response).to be_success
expect(response).to render_template("index")
end
end
end
The way I prefer:
In your controller_macros, replace the login_user with:
def login_user(user)
#request.env["devise.mapping"] = Devise.mappings[:user_confirmed]
sign_in user
end
Now, wherever you want to login user, you can do it something like:
RSpec.describe BallotsController, type: :controller do
describe "index" do
let(:user) { FactoryBot.create(:user) }
it "renders" do
login_user(user)
ballots_path
expect(response).to be_success
expect(response).to render_template("index")
end
end
# OR
describe "index" do
let(:user) { FactoryBot.create(:user) }
before(:each) do
login_user(user)
end
it "renders" do
ballots_path
expect(response).to be_success
expect(response).to render_template("index")
end
end
end
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
Hello so i keep getting this error, although i tried some of the solutions posted around here
spec/support/devise.rb
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
end
accounts_spec.rb
require "rails_helper"
RSpec.describe Api::V1::AccountsController, :type => :controller do
describe "GET index no account" do
it "has a 403 status code" do
get :index
expect(response.status).to eq(403)
end
end
describe "GET index with account" do
it "has a 200 status code" do
sign_in #user
get :index
expect(response.status).to eq(200)
end
end
end
accounts_controller.rb
class Api::V1::AccountsController < ApplicationController
skip_before_action :verify_authenticity_token
def index
#show user details
raise if not current_user
render json: { :user => current_user.as_json(:except=>[:created_at, :updated_at, :authorization_token, :provider, :uid, :id])}
rescue
render nothing: true, status: 403
end
Any ideas ? i'm blocked, the first test for 403 works, but then i just can't use the sign_in of devise ...
The problem was the devise.rb files. The correct config lines were
config.include Devise::TestHelpers, type: :controller
config.include Devise::TestHelpers, type: :view
Looks like the config is not being run.
Make sure you have your spec/support/devise.rb included by something like
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
in rails_helper.rb
Or move these config.includes into rails_helper itself
You can directly use This in your rails helper
config.include Devise::TestHelpers, :type => :controller
To use sign_in method.
I have started rspec coding recently and i am new to rails framework, rspec fails where i am using 'current_user' in controller. Please check below for my controller and rspec code. Thanks in advance.
Controller code:
def task
#tasks = current_user.alerts.where(kind: "TASK")
end
rspec code:
describe "get #task" do
it "assigns a task" do
sign_in(#user)
get :task
expect(response).to have_http_status(200)
end
You can do it like this:
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user) # Don't forget to create a factory for user
user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the "confirmable" module
sign_in user
It is better to put it in support/devise.rb:
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the "confirmable" module
sign_in user
end
end
end
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
you can say login_user instead of sign_in(#user)
Rails 3.2, RSpec 2.11. Controller macro isn't working, and it appears to be written correctly from all the research I've done. Here's the code
/spec/support/controller_macros.rb
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
#current_user = user
sign_in user
end
end
end
/spec/spec_helper.rb
RSpec.configure do |config|
....
config.extend ControllerMacros, :type => :controller
end
/spec/controllers/companies_controller_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'
describe CompaniesController, "index companies" do
context "for authenticated users" do
login_user
...
end
end
execution results:
undefined local variable or method 'login_user' for # (NameError)
Seems to have been answered here , you need to change your extend to an include
Adding the spec type fixed it for me:
Before:
describe Api::FooController do
.
.
end
After:
describe Api::FooController, type: :controller do
.
.
end