How to Stub out Warden/Devise with Rspec in Capybara test - ruby-on-rails

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

Related

Rails 4 devise integration testing. Persistent user?

I'm using devise for authentication in a small web application, and I'm having a few problems writing some integration tests.
The tests are going to be simple, such as
login works with valid credentials
login rejected with invalid credentials
...
using the techniques that were described in the rails tutorial, but rather than against a home grown authentication system, I'm attempting to retrofit it against devise.
I can use the sign_in function without any problems, and I'm doing that in one or two of my controller tests, e.g.
require 'test_helper'
class mySimpleControllerTest < ActionController::TestCase
include Devise::TestHelpers
def setup
#user = User.create!(
:firstname => "ANOther",
:surname => 'Person',
:username=> 'aperson',
:email => 'aperson#example.com',
:password => 'pass123',
:password_confirmation => 'pass123'
)
sign_in #user
end
test "should get home" do
get :home
assert_response :success
assert_select "title", "TEST PAGE"
end
end
that works wonderfully well. The problem I have is my integration tests for testing the login functionality. I don't want both setup and teardown functions in there, since some tests will have to check against logged out behaviour, some against logged in, and others against reset password etc.
the following test is always is responding with invalid username and password, even though the passwords are correct. Eventually, I want this test to pass when the username or password is incorrect, but right now it responds this way regardless of whether it is or it isn't.
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
test "should be redirected if root_path is called when logged out" do
get root_path
assert_response :redirect
end
test "login with invalid information" do
get new_user_session_path
assert_template 'devise/sessions/new'
post user_session_path, 'user[email]' => 'aperson#example.com', 'user[password]' => 'pass123'
...
I'm assuming the reason for this is because my test database doesn't contain a real user, in fact, the users table is currently empty. Which makes sense, since no user, it should respond with invalid username or password. However, if that is the case, how can I guarantee that the test database is populated with this default user when calling rake test?
After attempting several ways of getting this to work, I eventually moved all tests over to RSpec and Capybara, which will allow me to post data to forms.
I did test whether or not there was an issue with persistent users and this seemed to not be the case. The problem seemed to be that
post user_session_path, 'user[email]' => ...
didn't seem to actually post anything.

rails Devise authenticated routes in integration test

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

Rspec - Accessing Sorcery methods/variables

Trying to write some tests for code I've already written, with a view to extending my code using test-driven development.
I have a controller whose index action calls a 'user_info' method, which just collects together some instance variables relying on Sorcery's current_user variable. For example:
def user_info
#current_A = current_user.a
#current_B = current_user.b
end
def index
user_info
// rest of the method goes here
end
I started writing some tests using rspec, just to get a feel for testing this code base. My controller spec is very basic and looks like this:
describe MyController do
describe "GET 'index'" do
get 'index'
response.should be_success
end
end
However, I get the following error when I try to run this spec:
NoMethodError: undefined method 'a' for false:FalseClass
First of all, how do I get my spec to recognize the Sorcery method current_user? And, out of curiosity, why is current_user being flagged as an instance of FalseClass? If it's not calling the Sorcery method, (and I haven't defined current_user anywhere else in my code), should it not appear as nil?
To use Sorcery test helpers you need the following lines in your spec_helper.rb.
The following needs to be in the Rspec.configure block:
RSpec.configure do |config|
config.include Sorcery::TestHelpers::Rails
end
After you have this in place you can use the Sorcery test helpers. For a Controller test you would add the following to your test.
#user = either a fixture or a factory to define the user
login_user
If you don't want to specify #user you can pass an argument.
login_user(fixture or factory definition)
Once you login the current_user should be available to your tests.
logout_user is also available.
See the Sorcery Wiki for information on setting up a user fixture to work with the login_user helper.
Richard, the problem is likely that you don't have a current_user.
To do that, you need to simulate the login process.
You can do that with a controller spec, but I don't have a good example here. I was writing specs on existing code, like you, and it made sense to use request specs instead.
I also don't have one for Sorcery (I should!!) and I am here using Capybara for filling in forms,. Still, here is how my spec looked:
(Here :account is the same as :user would be)
context "when logged in" do
before :each do
#account = Factory.create(:account)
#current_game = Factory(:game_stat)
visit login_path
fill_in 'Username or Email Address', :with => #account.email
fill_in 'Password', :with => #account.password
click_button('Log in')
end
So factories are another matter, mine looked like this:
Factory.define :account do |f|
f.sequence(:username) { |n| "ecj#{n}" }
f.sequence(:email) { |n| "ecj#{n}#edjones.com" }
f.password "secret"
f.password_confirmation {|u| u.password }
end
You don't have to use factories, but you do need to get that session and current_user established.
On important bit is to ensure the user is activated after creation if you're using the :user_activation submodule of Sorcery.
So, if you're using the fabrication gem, that would look like,
Fabricator(:properly_activated_user, :from => :user) do
after_create { |user| user.activate! }
end
As #nmott mentioned you need to do two things:
1) Register text helper methods using:
RSpec.configure do |config|
config.include Sorcery::TestHelpers::Rails
end
2) In your example access current_user through controller.current_user like that:
login_user(user)
expect(controller.current_user).to be_present

Integration test with rspec and devise sign_in env

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.

Rspec2 controller testing with devise

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).

Resources