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.
Related
I have never dabbled outside of model testing when it comes to testing, and I am currently learning how to create my own user authentication instead of relying on Devise. It has been a little bit of time since I have worked with RSpec and not only would I like a little sanity check for syntax, but I can not figure out a way to confirm that my log in and sign up is indeed disappearing when a user logs in.
Here is my current users_logins_spec.rb
require 'rails_helper'
RSpec.describe "UsersLogins", type: :request do
before(:each) do
#user = FactoryGirl.build(:user)
end
it "login with invalid information" do
get login_path
expect(response).to render_template(:new)
post login_path, session: { email: "", password: "" }
expect(response).to render_template(:new)
expect(flash).to be_present
get root_path
expect(flash).not_to be_present
end
it "login with valid information" do
get login_path
post login_path, session: { email: #user.email, password: "password"}
expect(response).to redirect_to(#user)
follow_redirect!
expect(response).to render_template('users/show')
# expect(page).to have_selector('a', login_path)
end
end
Emphasizing the last test because that is the one that fails. I believe that if I were to put ID's on the tags that I want to check I would be able to circumvent the problem that I am having with methods that I understand. My intention is to learn how to manipulate my tests without having to find workarounds that change my code outside of the test, despite how little of a change that would be.
The other question is dealing with redirects. When I want to redirect to the #user url_path of #user, how does RSpec different when interpreting the call? I know that in Rails if I had something like
= link_to "Profile", current_user
it would automatically interpret it as
= link_to "Profile", user_path(current_user)
assuming my user resources within routes.rb.
If anyone can recommend some good tutorials for Rspec with Capybara for Integration and Feature testing that would be awesome, and any help/advice would be greatly appreciated. I am trying to make this as a Integration test instead of a feature test (which to my understanding those are kept within the requests directory and are "less readable" because they aren't so much as user stories but still are checking functionality of the site)
EDIT:
So I figured out part of my problem. I put in a debugger and was able to figure out that my user wasn't actually logging in correctly.
Here is the method that I am using to digest a password within the factory.
user.rb
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
factories.rb
FactoryGirl.define do
factory :user do
sequence(:id) { |n| n }
sequence(:name) { |n| "foo#{n}" }
email { "#{name}#example.com" }
password_digest User.digest('password')
end
end
The problem seems to be that my user login credentials are invalid and I am not exactly sure why.
FINAL EDIT - SOLVED
Okay, so I got it working. My problem with the user being incorrect was an easy fix. Instead of using password_digest within the factory I just did changed it to password and password_confirmation and it began the redirect. I originally had FactoryGirl.create(user) and have been switching between the two throughout testing, but in order for this to work with the confirmation it had to be create.
The next issue was actually with assert_select.
Here is the error:
NotImplementedError:
Implementing document_root_element makes assert_select work without needing to specify an element to select from.
I did end up finding a solution. Apparently this is with the latest version of RSpec and the solution that I had found was to set the document_root_element.
Within my spec/support I created a module
**spec/support/assert_select_root.rb
module AssertSelectRoot
def document_root_element
html_document.root
end
end
RSpec.configure do |config|
config.include AssertSelectRoot, :type => :request
end
I guess this was required for tests within spec/requests tests
Joe. You should keep in mind that FactoryGirl.build do not create database instance. So your Users table may be empty if you don't seed it before test.
I suggest you to use .create instead of .build.
I was looking at this answer to see how to test a session controller and wrote something like this:
require 'spec_helper'
describe SessionsController do
context "We should login to the system and create a session" do
let :credentials do
{:user_name => "MyString", :password => "someSimpleP{ass}"}
end
let :user do
FactoryGirl.create(:user, credentials)
end
before :each do
post :create , credentials
end
it "should create a session" do
puts user.inspect
puts session[:user_id]
#session[:user_id].should == user.id
end
end
end
Based on that I created a factory girl user:
FactoryGirl.define do
factory :user, :class => 'User' do
name "sample_user"
email "MyString#gmail.com"
user_name "MyString"
password "someSimpleP{ass}"
end
end
Now it all works - exceot for the before :each do statement - it never "logs" the "user" in - thus I cannot test the controllers functionality of, is a session properly created?
Now most would say, use capybara and test it through that way - but that's wrong, IMO - sure if I'm doing front end testing that would work, but I'm testing controller based logic. Can some one tell me why this isn't working? routing works fine.
My puts session[:user_id] is coming up nil, when it shouldn't
let is lazily evaluated, even for the before clause, so the user has not been created as of the time you do the post to login. If you change to using let!, you'll avoid this problem.
You misunderstood SessionsController and RegistrationsController.
A Session is for an user who has already registered, not for creating an user. #create in SessionController means to create a session, not an user.
RegistrationController is for creating user with full details including password_confirmation.
To test SessionsController, you need to create a valid user in FactoryGirl at first, then use his credentials say email and password to sign in.
I cant find solution for my problem: cant login user in rspec test.
I tried this code, following by this link - https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs
require 'spec_helper'
require 'factory_girl_rails'
describe "ololo" do
it "blocks unauthenticated access" do
user = User.create(email: "lol#mail.com", password: "123456")
request.env['warden'].stub(:authenticate!).
and_throw(:warden, {:scope => :user})
visit "/tasks"
page.should have_content("Signed in successfully.")
end
end
/error/
Failure/Error: page.should have_content("Signed in successfully.")
expected there to be text "Signed in successfully." in "Task App Home Login You need to sign in or sign up before continuing. Sign in Email Password Remember me Sign upForgot
your password? Task App by Denys Medynskyi"
also tried this link - http://codingdaily.wordpress.com/2011/09/13/rails-devise-and-rspec/.
Devise wiki tutorial is also not working for me.
Please, I'm stuck hardly. Help me anyone.
Without seeing your error message, I guest one possible reason is you created hard record of User to test db. You may have run this test once, so in second time this creation fails because record with same email exists.
You've required FactoryGirl. Why don't you use FactoryGirl to create the fixture data?
In spec/factories/user.rb
FactoryGirl.define do
factory :user do
email: "lol#mail.com"
password "123"
end
end
In your test, write
FactoryGirl.create(:user)
Add
Comparing with the tut you lack two steps:
You need to use FactoryGirl to replace the hard created record, as writing above.
You need to execute the sign_in step before visit the private page.
Maybe you would be good with #2 only if you cleared the db after tests. Anyway factory data is recommended over hard data.
I have a request spec that is trying to test file download functionality in Rails 3.1 for me. The spec (in part) looks like this:
get document_path(Document.first)
logger(response.body)
response.should be_success
It fails with:
Failure/Error: response.should be_success
expected success? to return true, got false
But if I test the download in the browser, it downloads the file correctly.
Here's the action in the controller:
def show
send_file #document.file.path, :filename => #document.file_file_name,
:content_type => #document.file_content_type
end
And my logger gives this information about the response:
<html><body>You are being redirected.</body></html>
How can I get this test to pass?
Update:
As several pointed out, one of my before_filters was doing the redirect. The reason is that I was using Capybara to login in the test, but not using it's methods for navigating around the site. Here's what worked (partially):
click_link 'Libraries'
click_link 'Drawings'
click_link 'GS2 Drawing'
page.response.should be_success #this still fails
But now I can't figure out a way to test the actual response was successful. What am I doing wrong here.
Most likely, redirect_to is being called when you run your test. Here's what I would do to determine the cause.
Add logging to any before filters that could possibly run for this action.
Add logging at several points in the action itself.
This will tell you how far execution gets before the redirect. Which in turn will tell you what block of code (probably a before_filter) is redirecting.
If I had to take a guess off the top of my head, I'd say you have a before_filter that checks if the user is logged in. If that's true, then you'll need to make sure your tests create a logged-in session before you call the login-protected action.
I was getting the same redirect until I realized that my login(user) method was the culprit. Cribbed from this SO link, I changed my login method to:
# file: spec/authentication_helpers.rb
module AuthenticationHelpers
def login(user)
post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end
end
In my tests:
# spec/requests/my_model_spec.rb
require 'spec_helper'
require 'authentication_helpers'
describe MyModel do
include AuthenticationHelpers
before(:each) do
#user = User.create!(:email => 'user#email.com', :password => 'password', :password_confirmation => 'password')
login(#user)
end
it 'should run your integration tests' do
# your code here
end
end
[FWIW: I'm using Rails 3.0, Devise, CanCan and Webrat]
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