I am using devise configured to use omniauth facebook sign in integration.
When calling the sign_in method from my spec/request tests I get:
undefined method `env' for nil:NilClass
spec:
describe FacebookController do
include Devise::TestHelpers
it "should display facebook logged in status" do
#user = User.create(:id => "123", :token => "token")
sign_in #user
visit facebook_path
end
end
Your code looks a lot like mine - I was trying to use Capybara and the Devise TestHelper functions, and it turns out you can't, per https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara. The recommended way to do it is explained on that page, and it worked for me.
To be clear, here's what I did - in spec_helper.rb:
RSpec.configure do |config|
config.include Warden::Test::Helpers
end
Warden.test_mode!
And in my code, simply - logout :user.
Here's why, according to the Devise wiki, you cannot use sign_out:
If you're wondering why we can't just use Devise's built in sign_in and sign_out methods, it's because these require direct access to the request object which is not available while using Capybara. To bundle the functionality of both methods together you can create a helper method.
Which, roughly, means that whereas with, say, MiniTest, an object representing the request (#request) is added as an instance variable to the test case class, that doesn't happen with Capybara. I haven't looked at the code to know the details more exactly but basically, Warden expects to find this object to then access the cookie store where the sign in credentials are. With Capybara/RSpec, I expect this isn't happening.
Related
In my spec_helper.rb I've added
config.include Devise::TestHelpers, :type => :controller
In controller spec before :all hook I'am trying to sign in user with this code
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:confirmed_user)
sign_in user
and get NoMethodError: undefined method `env' for nil:NilClass.
Any ideas?
I've changed before(:all) to before(:each), and now it works. Looks like API incompatibility between devise (used github master branch) and rspec 2.14.1.
Comment out the #request.env["devise.mapping"] = Devise.mappings[:user] line if you are writing specs for internal controllers of Devise. Referenced from the test helpers section in the Devise docs.
The controller spec will have a request object in it. You should probably change from #request to request..
request.env["devise.mapping"] = Devise.mappings[:user]
#request.env["devise.mapping"] = Devise.mappings[:user] is only needed when you are testing a Devise controller or a controller that inherits from a Devise controller, as stated in the Devise documentation:
If you are testing Devise internal controllers or a controller that inherits from Devise's, you need to tell Devise which mapping should be used before a request. This is necessary because Devise gets this information from the router, but since controller tests do not pass through the router, it needs to be stated explicitly.
If you are not in the case mentioned above you don't need to add it.
If you are in the case mentioned above but #request is nil, try adding these helpers to your rails_helper.rb:
config.include Devise::Test::ControllerHelpers, type: :controller
I want to test every route in an application, and learned I should do that in an integration test: Where to test routes in ruby on rails
However I'm getting the following error:
NoMethodError: undefined method `authenticate?' for nil:NilClass
/usr/local/Cellar/ruby/1.9.3-p194/lib/ruby/gems/1.9.1/gems/devise-2.1.2/lib/devise/rails/routes.rb:286:in `block in authenticated'
It's well-mentioned online that you can't use Devise::TestHelpers in integration testing --
Devise Google Group,
Devise Github page
How can I test routes like the following?
# config/routes.rb
devise_for :users
authenticated :user do
root to: 'static#home'
end
root to: 'static#landing'
I am running test unit tests with $ rake test:integration
Devise::TestHelpers work by putting things directly into your session. When running integration tests with Capybara, you don't have access to the server-side session. You just have access to the browser.
In our application, our integration tests use helper methods like this, that interact with Devise through the user interface:
def authenticate(user, password = nil)
password ||= FactoryGirl.attributes_for(:user)[:password]
visit new_user_session_path
fill_in 'email', with: user.email
fill_in 'password', with: password
click_on 'Login'
expect(current_path).to eq welcome_path
end
Integration tests are important for your application work flow. They can tell about your URL definition more clearly.
Check the post by nicholaides, that explains the cause of this error and the solution in Authenticated routes.
Still the problem is:
Devise has its own methods and you can't use Devise::TestHelpers in ruby. So how can you test? Well you need to include the Devise::TestHelpers somehow.
Well, if you're using RSpec, you can put the following inside a file named spec/support/devise.rb:
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
This is specified here.
But wait.......... Again, you can run into this same issue with Test::Unit.
Then?
So, you just need to add the devise test helpers to test/test_helper.rb.
class ActiveSupport::TestCase
include Devise::TestHelpers
end
I am trying to write a integration test for signing in with twitter using OmniAuth and Devise. I am having trouble getting the request variable to be set. It works in the controller test but not the integration test which leads me to think that I am not configuring the spec helper properly. I have looked around, but I can't seem to find a working solution. Here is what I have so far:
# spec/integrations/session_spec.rb
require 'spec_helper'
describe "signing in" do
before do
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
visit new_user_session_path
click_link "Sign in with twitter"
end
it "should sign in the user with the authentication" do
(1+1).should == 3
end
end
This spec raies a error before it can get to the test and I am not quite sure where the request variable needs to be initialized. The error is:
Failure/Error: request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
NoMethodError:
undefined method `env' for nil:NilClass
Now I use the request variable in my controller spec and the test pass but it is not being initialized for the integration tests.
# spec/spec_helper.rb
Dir[Rails.root.join("spec/support/*.rb")].each {|f| require f}
...
# spec/support/devise.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Thanks for the help!
Capybara README says "Access to session and request is not possible from the test", so I gave up to configure in test and decided to write a helper method in application_controller.rb.
before_filter :set_request_env
def set_request_env
if ENV["RAILS_ENV"] == 'test'
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
end
end
The Devise test helpers are only meant to be used in controller specs not integration specs. In capybara there is no request object so setting it won't work.
What you should do instead is scope loading of Devise test helpers to your controller specs, something like this:
class ActionController::TestCase
include Devise::TestHelpers
end
and use the warden helper for capybara specs as suggested in this guide: https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
For a more detailed discussion look at this github issue page: https://github.com/nbudin/devise_cas_authenticatable/issues/36
This one works for me during test using rspec + devise + omniauth + omniauth-google-apps. No doubt the twitter solution will be very similar:
# use this method in request specs to sign in as the given user.
def login(user)
OmniAuth.config.test_mode = true
hash = OmniAuth::AuthHash.new
hash[:info] = {email: user.email, name: user.name}
OmniAuth.config.mock_auth[:google_apps] = hash
visit new_user_session_path
click_link "Sign in with Google Apps"
end
When using request specs with newer versions of RSpec, which do not allow access to the request object:
before do
Rails.application.env_config["devise.mapping"] = Devise.mappings[:user] # If using Devise
Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
end
I want to stub out a logged in user (with Devise/Warden) using rspec mocks in a Capybara test suite in my Rails app. This would save a ton of time, and would mean that my test suite can/will be run regularly.
Previously I was able to do this using authlogic by stubbing out my session model with some code like this:
def login(user)
user_session = mock_model(UserSession, {:user => user})
UserSession.stub(:find).and_return(user_session)
end
Now that i'm using Devise, i no longer have access to a UserSession object. And since i'm using capybara to test my code, i don't have direct access to the request object to use devise's built in sign_in test helper.
My question is: how can I simulate a logged in user with capybara, devise, and spec mocks without requiring every scenario with a logged in user to first go to the sign up path, fill in the form, submit, wait for response, and then go to the desired page?
Warden comes with built in test helpers. It allows you to login without having to use the UI in your cucumber tests. Just add the files below into your project.
# features/support/warden.rb
Warden.test_mode!
World Warden::Test::Helpers
After { Warden.test_reset! }
# features/step_definitions/user_steps.rb
Given /^I am logged in as a user$/ do
#current_user = User.create!(:username => 'user', :password => 'password')
login_as(#current_user, :scope => :user)
end
Use Wardens.test_mode! with Capybara
I'm currently new to RSpec and trying to implement some Controller testing with RSpec
In my Rails app, I'm using Devise as my authentication system. My question is, When we test a controller which uses some authentication system (in my case Devise), what is the standard practice?
Is it
1 - to skip the authentication
or
2 - to authenticate the controller
as per the question, following is my controller
require File.dirname(__FILE__) + '/../spec_helper'
describe ProjectsController do
include Devise::TestHelpers
p "starting..."
before(:each) do
p "in before method"
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in Factory.create(:user)
end
it "should create a project" do
p "should create a project"
end
after(:each) do
#user.destroy unless #user.nil?
end
end
I can only see 'starting', But why its not going to "in before method" and "should create a project"
I'm using Rspec2 and Rails2 on Ubuntu.
Check this: Stubbing Devise in rSpec and Rails3.
Standard practice is not skipping authentication, but effectively making sure that a correct user is logged in (for devise).
Referring to your code: have you tried to create some real test? E.g. something as simple as
it "gets index" do
get :index
response.status.should be == 200
end
I am not sure why you are not seeing the print-statements. Either rspec skips the empty step (there is no real code), or because something else went wrong. But honestly, I am not even sure if using p inside rspec works.
A tool like rubymine allows you to easily debug your specs if you want to step into it (which imho is a better approach then the scattered p statements).