I try to test devise user authentication, the problem I've done everything according to samples, however the code still doesn't work.
spec/support/devise/devise_support.rb
module ValidUserRequestHelper
def sign_in_as_a_valid_user
#user ||= Fabricate(:simple_user)
post_via_redirect user_session_path, 'user[email]' => #user.email, 'user[password]' => #user.password
end
end
RSpec.configure do |config|
config.include ValidUserRequestHelper, :type => :request
end
spec/spec_helper.rb
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
However when I run test, it fails on the calling to `sign_in_as_a_valid_user'
undefined local variable or method `sign_in_as_a_valid_user' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0xc57a0c4>
I don't have idea how to debug this.
The test code is
require 'spec_helper'
describe User do
before do
sign_in_as_a_valid_user
end
...
when you write this in your rspec configuration
RSpec.configure do |config|
config.include ValidUserRequestHelper, :type => :request
end
you tell rspec to only include this helper for request spec. those are typically located in spec/request. deriving from the example of your spec that has describe User in it, i assume that you are writing a model spec, typically located in spec/model. so when running the spec, rspec won't include it for that spec!
if you just remove the :type => :request it will get included everywhere. keep in mind, that there is usually a good reason for this kind of restrictions. for example a helper that only works with a fake browser, like it is done in request specs.
Related
I would like to add a module that includes a method to help me log in as a user. Here it is:
module TestHelper
require 'spec_helper'
ALL_USERS = ["administrator", "instructor", "regular_user"]
def self.login_as(user_type)
user = User.find_by(global_role_id: GlobalRole.send(user_type))
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in user
end
end
The spec that's calling it is
require 'spec_helper'
RSpec.describe QuestionsController, :type => :controller do
include Devise::TestHelpers
include TestHelper
describe "a test" do
it "works!" do
TestHelper.login_as("administrator")
end
end
end
And here is the spec_helper
RSpec.configure do |config|
config.include TestHelper, :type => :controller
The error I get is: undefined method 'env' for nil:NilClass It looks like my module doesn't have access to #request.
My question is: how do I access and #request in the external Module?
In addition to the other answers, you should consider only including this module for relevant spec types using the following code:
config.include TestHelper, type: :controller # Or whatever spec type(s) you're using
You could pass #request in to TestHelper.login_as from the tests. Eg
module TestHelper
def self.login_as(user_type, request)
...
request.env['devise.mapping'] = Devise.mappings[:user]
...
end
end
...
describe 'log in' do
it 'works!' do
TestHelper.login_as('administrator', #request)
end
end
But probably better to follow the macro pattern from the devise wiki if other devise mappings in play.
I have written some controller tests in a Rails app that uses Devise and Rspec. Following this guide, I've created a controller_macros.rb in the /spec/support/ directory. There is also a devise.rb file in the same directory, with:
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
config.extend ControllerMacros, :type => :controller
end
Both files are being required in the spec_helper.rb file, with this line:
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
Now here is what is weird: this approach works fine on an OS X laptop, but fails in my Linux desktop. They both use the same RVM settings, same gemsets, same everything.
The error I get when running the tests in Linux is:
uninitialized constant ControllerMacros (NameError)
Obviously the controller_macros.rb module is failing to load in Linux. I've seen SO answers suggesting that config.extend could be changed to config.include, but that doesn't fix the problem.
Any ideas where I can look or what I can test to help isolate the issue?
I'm using Rails 4.1.8 and Rspec 3.1.7.
I struggled with this as well. Answers just weren't working for me. This is what I did (Ubuntu, Rails 4, Rspec 3):
spec/rails_helper.rb
# <snip> env stuff
require 'spec_helper'
require 'rspec/rails'
require 'devise'
require 'support/controller_macros'
# <snip> some non-devise stuff
RSpec.configure do |config|
# <snip> some more non-devise stuff
config.include Devise::TestHelpers, type: :controller
config.include ControllerMacros, type: :controller
end
spec/support/controller_macros.rb
module ControllerMacros
def login_user
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
sign_in user
end
end
students_controller.rb
require "rails_helper"
describe StudentsController, type: :controller do
before do
login_user
end
describe "GET index" do
it "has a 200 status code" do
get :index
response.code.should eq("200")
end
end
end
I solved this by adding
require Rails.root.join("spec/support/macros/controller_macros.rb")
to the top of my spec/support/devise.rb file
When adding macros to Rspec's config, you have to specify the type of test it'll be accessed by. For instance, you might type:
config.extend ControllerMacros, :type => :controller
How do you get this to work with Capybara, whose type (:feature) is seemingly not recognized by Rspec's config. Trying something like this does not work:
config.extend FeatureMacros, :type => :feature
I don't know why you type extend, all my settings are include and they works.
RSpec.configure do |config|
# ... others
# Session helpers - For Capybara
config.include Features::SessionHelpers, type: :feature
# Controller helpers
config.include ControllerMacros, type: :controller
end
And the module files are in spec/support. If sub module, they are in sub folder like `spec/support/features/
Example of Capybara helpers
# spec/support/features/session_helpers.rb
require 'spec_helper'
module Features
module SessionHelpers
def user_sign_in
end
end
end
I have the following sample code that is using a vanilla spec_helper. I'm trying to use the sign_in_as but am getting an error saying it doesn't exist. I hadn't really seen this config syntax before so I'm not sure if this is being used right. Any help would be appreciated? thx
in helpers/sign_in_helpers.rb
module SignInHelpers
def say_hello
puts 'hello'
end
def sign_in
sign_in_as 'person#example.com'
end
def sign_in_as email
visit root_path
fill_in 'Email address', with: email
click_button 'Sign In'
end
end
RSpec.configure do |config|
config.include SignInHelpers
end
Some errors in your code.
you should have this code on top of the file
require 'spec_helper'
The place to put the file is spec/support, not spec/helpers
Also, for the following code, better to keep them all inside spec/spec_helper.rb inside the configure block, though your way should work as well.
RSpec.configure do |config|
config.include SignInHelpers
end
By the way, better to define the type as your helper are for features only, and not going to work in controller tests.
config.include SignInHelpers type: :feature
In theory, yes. Your call looks like a valid use of the include syntax
https://www.relishapp.com/rspec/rspec-core/docs/helper-methods/define-helper-methods-in-a-module
According to this from the devise wiki I should be able to use a login_user helper method in my controller tests. Accordingly I have the following within the spec directory:
#spec_helper.rb
...
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
...
and
#support/controller_macros.rb
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Factory.create(:user)
sign_in #user
end
end
end
however calling the helper doesn't work:
#requests/some_spec.rb
require 'spec_helper'
describe "GET /guides/edit" do
login_user
end
Can someone point toward where I'm going wrong. The test suite works about from this. I get a undefined local variable or method message so I guess the module isn't being included.
Rails 3.0.7
rspec 2.6.0
devise 1.3.4
backtrace
I imagine there are a couple of problems with this approach. First is that you're using request specs, not controller specs, so the login_user method is not made available by config.extend ControllerMacros, :type => :controller. Second, even if you are able to include the method it most likely won't work anyway, since the Devise test helpers are specifically written for controller/view tests, not integration tests.
Take a look at David Chelimsky's answer to this SO question, which may be of help.
I can't answer for sure... but the code smell for me is the "before(:each)" defined inside the helper. why don't you try:
#support/controller_macros.rb
module ControllerMacros
def login_user
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Factory.create(:user)
sign_in #user
end
end
and
#requests/some_spec.rb
require 'spec_helper'
describe "GET /guides/edit" do
before(:each) do
login_user
end
end
and if that fails - maybe it just can't find #request - in which case, pass it as a variable to login_user
Edit:
Looks like you might need to include the devise test helpers.
The rdoc says you should have this file:
# spec/support/devise.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Not sure if that differs from how you've already got it in spec_helper.rb
... looks pretty similar to me.
I have same issue with Rails 3.0.10 rspec 2.6.0 devise 1.3.4 spork-0.9.0.rc9 on my controller specs, i have changed config. extend to config.include and its work !
Forget to confirm if your app is not confirmable. Your code should look like
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