I have profiled my feature files. I have found out that my login step takes the most time.
Given /^I am logged in as "(.+)"$/ do |login|
visit path_to('the home page')
fill_in "login", :with => login
fill_in "password", :with => 'foobar'
click_button "loginButton"
end
It takes over 5 seconds on my development box.
I want to make another step with login functionality, but without filling out the form, simply set the session, and used it in my other tests as background scenario.
Given /^I am logged in as "(.+)" through session$/ do |login|
user= User.find_by_login(login)
end
The above step finds the user, but how I can make it stores the session, and redirects me ?
you could just avoid authenticating in the other steps, probably not what a die hard cucumber dev would say. But if you have the authentication workflow tested elsewhere, then I dont see the harm. I didn't try this, but I think the variable scope should be right.
Given /^I am logged in as "(.+)" through session$/ do |login|
#user= User.find_by_login(login)
#open the class and spike
class ApplicationController < ActionController::Base
def current_user
#user
end
end
end
Related
using rspec with the default driver is working fine and all tests are passed.
changing driver: :webkit will have a bad side-effect.
step: the user is logged in
step: visiting root_path with a session (current_user)
step: visiting root_path without a session (current_user = nil)
so either after the first visit root_path or before the second, the session is killed or whatever - we can't get the user to stay logged in.
test looks like this
scenario 'something', driver: :webkit do
user = FactoryGirl.create :user
login_as(user)
visit root_path
visit root_path
end
is this a known bug? are there any workarounds or are we missing something ?
as requested:
def login_as(user)
visit root_path
click_on "Login"
fill_in "user[login]", with: user.username
fill_in "user[password]", with: user.password
click_on "Sign in"
end
The default driver runs everything synchronously --- drivers that use real browsers and support javascript do not necessarily do things synchronously - so it's possible in drivers other than rack-test for click_on 'Sign in' to return immediately. Therefore if you're not checking for content that would be seen on success the next visit root_path can get executed immediately and cancel the submission of the login form. To fix that add something like
expect(page).to have_content('You are now logged in') # whatever text is shown on a successful login
as the last line of your login_as method. This is not normally an issue for most people, because after logging in the next step is usually to click on something on the page, which will make Capybara wait for that item to appear thereby waiting for the login to complete.
If that is not what is happening here then the only place (given your sample code) that can be logging the user out is your own app
if you are deleting the session after the first visit, it is expected that the user logout.
Does visiting without session means that visiting the page as anonymous?
I have a rails app that includes a blog feature.
In one single feature test (using Capybara::Rails::TestCase, with ruby tests (asserts/refutes) i.e, not spec) for the blog, I want to test adding a post, adding comments, editing the post, etc. as individual tests - each of these tests builds upon the last one, as the post created in the first test is commented on in the second test, and so on.
I have seen posts which show workarounds for doing this in a unit test (global variables, use setup/teardown), but I wondered if there is a more direct way to do it in a feature test, since it is likely more common here.
Ideally, I want the login session to persist, as well as database records created in previous tests to persist across each test in the TestCase. Setup and teardown could be used to login each time, but not the intermediate records created for posts, comments, etc.
I want something like:
class BlogTest< Capybara::Rails::TestCase
test 'can sign in' do
user = User.create!(name: "user",
email: "user#example.com",
password: "passw0rd!", password_confirmation: "passw0rd!")
visit new_user_session_path
fill_in('Login', :with => user.email)
fill_in('Password', :with => user.password)
check('Remember me')
click_button('Sign in')
end
test 'can create post' do
visit new_post_path # how can I have user logged in?
fill_in "Title", with: "My first post title!"
fill_in "Body", with: "My first post body!"
click_button "Publish"
end
test 'can comment on post' do
visit post_path(Post.first) # should go to post created in last test
click_button "Add comment"
...
end
end
I have heard that this may be possible in Cucumber, but chose not to use Cucumber for other reasons, so want it to work with Minitest and Capybara.
Capybara::Rails::TestCase inherits from ActiveSupport::TestCase. One of ActiveSupport::TestCase's main features is that it runs each test in a database transaction. There are ways to work around this, but I would not recommend them.
Instead, I suggest you work with the behavior of the rails test classes. In this case, you want to share actions between tests. I recommend you extract those actions into methods and call those methods in your tests. Here is how I would implement this with your test code:
class BlogTest< Capybara::Rails::TestCase
def user
#user ||= User.create!(name: "user",
email: "user#example.com",
password: user_password,
password_confirmation: user_password)
end
def user_password
"passw0rd!"
end
def sign_in(email, password)
visit new_user_session_path
fill_in('Login', :with => email)
fill_in('Password', :with => password)
check('Remember me')
click_button('Sign in')
end
def create_post(title = "My first post title!",
body = "My first post body!")
visit new_post_path # how can I have user logged in?
fill_in "Title", with: title
fill_in "Body", with: body
click_button "Publish"
end
def comment_on_post(post, comment)
visit post_path(post)
click_button "Add comment"
# ...
end
test "can sign in" do
sign_in(user.email, user_password)
# add assertions here that you are signed in correctly
end
test "can't sign in with a bad password" do
sign_in(user.email, "Not the real password")
# add assertions here that you are not signed in
end
test "can create post when signed in" do
sign_in(user.email, user_password)
create_post
# add assertions here that post was created correctly
end
test "can't create post when not signed in" do
create_post
# add assertions here that post was not created
end
test "can comment on post when signed in" do
sign_in(user.email, user_password)
create_post
post = user.posts.order(:created_at).last
comment_on_post(post, "I can comment because I'm signed in!")
# add assertions here that comment was created correctly
end
test "can't comment on post when not signed in" do
post = Post.first
comment_on_post(post, "I can't comment because I'm not signed in!")
# add assertions here that comment was not created
end
end
Each action has a good name, and you can reuse those actions for different types of tests. Each test is executed within a database transaction, so each time the each test method is run the database looks the same.
I am using Rails 3.1.0 and the recaptcha gem from here. I was running a cucumber test that checks that users can sign up. Sign up requires users to fill out the captcha. I know that my test does not touch the captcha:
When /^I create a new account with email: "(.*?)" and password: "(.*?)"$/ do |email, pw|
click_link "Sign up"
fill_in "Email", :with => email
fill_in "Password", :with => pw
fill_in "Password confirmation", :with => pw
click_button "Sign up"
end
But the test still passes. I check success by verifying that the successful sign up message is present on the page and the recaptcha failure message is not present using this step:
Then /^I should (not )?see "(.*)"$/ do |negate, text|
if negate
page.should_not have_content text
else
page.should have_content text
end
end
The controller is almost identical to the first one suggested here.
class RegistrationsController < Devise::RegistrationsController
def create
if verify_recaptcha
super
else
build_resource
clean_up_passwords(resource)
flash.now[:alert] = "There was an error with the recaptcha code below. Please re-enter the code."
flash.delete :recaptcha_error
render :new
end
end
end
Is there any reason why recaptcha would not work in the test environment? It seems to work fine in development.
Recaptcha by default does not verify the captcha in the test and cucumber environments (see verify logic, configuration logic, and default value ). If it weren't for this, either testing would be very difficult or the captcha wouldn't be very useful.
Just to add to Bens answer and give a potential workaround:
To make sure things don't work (it "fails with recaptcha") in RSpec I've done
before do
Recaptcha.configuration.skip_verify_env.delete('test')
end
after do
Recaptcha.configuration.skip_verify_env << 'test'
end
Thanks Ben
Working with RSpec & Capybara, I'm getting an interesting test failure mode which goes away with a few subtle rearrangements of lines in the test case...stuff that shouldn't matter.
I'm developing my own authentication system. It is currently working and I can login/out with the browser and the session works etc etc. However, trying to test this is failing. Something is going on that I don't quite understand, which seems to depend on the order of (seemingly) unrelated calls.
require 'spec_helper'
describe "Sessions" do
it 'allows user to login' do
#line one
user = Factory(:user)
#For SO, this method hashes the input password and saves the record
user.password! '2468'
#line two
visit '/sessions/index'
fill_in 'Email', :with => user.email
fill_in 'Password', :with => '2468'
click_button 'Sign in'
page.should have_content('Logged in')
end
end
As is, that test fails...the login fails. After inserting 'debugger' calls into both the spec and the controller I can see why: the user is not getting inserted into the database as far as the controller is concerned:
Edit adding in ApplicationController
class ApplicationController < ActionController::Base
helper :all
protect_from_forgery
helper_method :user_signed_in?, :guest_user?, :current_user
def user_signed_in?
!(session[:user_id].nil? || current_user.new_record?)
end
def guest_user?
current_user.new_record?
end
def current_user
#current_user ||= session[:user_id].nil? ? User.new : User.find(session[:user_id])
rescue ActiveRecord::RecordNotFound
#current_user = User.new
flash[:notice] = 'You\'ve been logged out.'
end
end
class SessionsController < ApplicationController
def login
user = User.where(:email=>params[:user][:email]).first
debugger ###
if !user.nil? && user.valid_password?(params[:user][:password])
#engage session
else
#run away
end
end
def logout
reset_session
redirect_to root_path, :notice => 'Logget Out.'
end
end
in the console, at the above breakpoint:
1.9.2 vox#Alpha:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7
if !user.nil? && user.valid_password?(params[:user][:password])
(rdb:1) irb
ruby-1.9.2-p180 :001 > User.all.count
=> 0
ruby-1.9.2-p180 :002 >
However, if I rearrange a few lines in my test, putting line 'two' above line 'one':
describe "Sessions" do
it 'allows user to login' do
#line two
visit '/sessions/index'
#line one
user = Factory(:user)
#For SO, this method hashes the input password and saves the record
user.password! '2468'
fill_in 'Email', :with => user.email
fill_in 'Password', :with => '2468'
click_button 'Sign in'
page.should have_content('Logged in')
end
end
I get this in the console (same breakpoint as above):
1.9.2 vox#Alpha:~/Sites/website$ rspec spec/controllers/sessions_controller_spec.rb
/Users/vox/Sites/website/app/controllers/sessions_controller.rb:7
if !user.nil? && user.valid_password?(params[:user][:password])
(rdb:1) irb
ruby-1.9.2-p180 :001 > User.all.count
=> 1
For the sake of brevity I've omitted the full dump of the contents of the user object but I can assure you that the test completes as expected.
This behavior of swapping lines to get the test to pass doesn't really fit well with my idea of what should be going on with these commands and has proven to be quite a bear to my testing in other areas.
Any hints as to what is going on here?
I've scoured google and SO for ideas which present this problem, and there are no shortage of SO questions about RSpec/Capybara and Sessions. Nothing seemed to fit quite right though.
Thanks for looking.
Update
I've added a breakpoint (just before a visit call) and some debugging to the test and come back with this:
(rdb:1) user
#<User id: 1, login_count: 1, email: "testuser1#website.com", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">
(rdb:1) User.all
[#<User id: 1, login_count: 1, email: "testuser1#website.com", encrypted_password: "11f40764d011926eccd5a102c532a2b469d8e71249f3c6e2f8b...", salt: "1313613794">]
(rdb:1) next
/Users/vox/Sites/website/spec/controllers/sessions_controller_spec.rb:19
fill_in 'Email', :with => user.email
(rdb:1) User.all
[]
So clearly something along the way that visit does is telling Factory Girl that its done with the user object and so she deletes it?
Edit After inspecting test.log carefully, nothing is issuing any delete. So I'm more or less back to square one.
With the help of the Factory Girl mailing list I've found the issue.
By default RSpec uses transactions to maintain the database in a clean state and each transaction is tied to a thread. Somewhere along the pipeline the visit_page command splits off and the transaction tied to the current thread dies.
The solution is simple: disable transactions.
describe "Sessions" do
self.use_transactional_fixtures = false
it 'no longer uses transactions' do
#whatever you want
end
end
Update for Rails 5.1
As of Rails 5.1, use_transactional_fixtures is deprecated and should be replaced with use_transactional_tests.
self.use_transactional_tests = false
I think the user variable in RSpec has overwritten the one in the controller so it didn't work ? (couldn't get right user.email in the test)
Before :
user = Factory(:user)
user.password! '2468'
visit '/sessions/index' # user gets overwritten
fill_in 'Email', :with => user.email # can't get user.email
After :
visit '/sessions/index' # Execute action
user = Factory(:user) # user gets overwritten
user.password! '2468'
fill_in 'Email', :with => user.email # user.email works
This isn't technically an answer, more of a comment but to clarify the code it's the easiest mechanism.
Can you try doing the following to help narrow down where the user's being destroyed
describe "Sessions" do
it 'allows user to login' do
#line one
user = Factory(:user)
#For SO, this method hashes the input password and saves the record
user.password! '2468'
# check the user's definitely there before page load
puts User.first
#line two
visit '/sessions/index'
# check the user's still there after page load
puts User.first.reload
fill_in 'Email', :with => user.email
fill_in 'Password', :with => '2468'
click_button 'Sign in'
# check the user's still there on submission (though evidently not)
puts User.first.reload
page.should have_content('Logged in')
end
end
EDIT
The fact that it works for you ok in real life but not in Capybara suggests that it may be a product of existing session information. When you're testing in the browser you're usually going off the back of previous work but Capybara is always starting from a clean session.
You can easily see if you can reproduce the Capybara error in-browser by clearing all your cookies (as I'm sure you know) or by just switching to a new incognito window in Chrome/FF which is a nice quick way to get a clean session.
The correct answer above helped me. Of course, I needed to change some other tests that (wrongly or rightly) assumed a fixture did not exist. For more information: there's some info about this in the Capybara README.
https://github.com/jnicklas/capybara
"If you are using a SQL database, it is common to run every test in a transaction, which is rolled back at the end of the test, rspec-rails does this by default out of the box for example. Since transactions are usually not shared across threads, this will cause data you have put into the database in your test code to be invisible to Capybara."
You can also config RSpec to clean up after your test manually:
https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Cleanup-after-your-Rspec-tests
I am trying to run Cucumber tests against a database that is already populated with data. It's basically a copy of our production database.
For some reason I have some tests failing which seemingly is coming from the fact that it can't find the data. Any idea on how to make this work and why it wouldn't like "pre-seeded" databases?
Our data is far too complex to try to recreate via factories/fixtures. There's just no way.
Here's an example:
Scenario: a tech logs in
Given I am on the login page
And user with id 2328 exists
When I login as "user" with password "password"
Then I should be on the dashboard page
And I should see "Logged in as: USER"
The step:
Given /^user with id (\d+) exists$/ do |id|
Employee.exists? id
end
When /^I login as "([^\"]*)" with password "([^\"]*)"$/ do |username, password|
visit login_url
fill_in "username", with: username
fill_in "pwd", with: password
click_button "Login"
end
Maybe its my steps and has nothing to do with the database, but I can't understand why it doesn't fail on the And user with id 2328 exists step when I give it a bad number.
I have seen this but it doesn't seem to affect the flow.
Lastly, here is the actual login method (believe me, I know how awful this login check is, simply checking we have permissions to the database):
def login
if request.post?
# get database config for env
configs = ActiveRecord::Base.configurations[Rails.env]
begin
# attempt to connect to the db
plsql = plsql_connect(params[:username], params[:pwd])
# if no exception, get the employee ID and location
session[:employee_id] = plsql.hes.installer_web_pkg.get_employee_pk.to_i
session[:employee_location] = plsql.hes.installer_web_pkg.fn_get_truck_location(session[:employee_id]).to_i
# logout
plsql.logoff
# dashboard time!
redirect_to dashboard_url(session[:employee_id].to_i)
rescue Exception => err
# could not connect, fail!!
flash.now[:notice] = "Username/password not valid (or locked out of account). Try again or contact the HelpDesk."
render :action => "login"
end
end
end
Every time I run, I get:
expected: "/dashboard",
got: "/login" (using ==) (RSpec::Expectations::ExpectationNotMetError)
./features/step_definitions/web_steps.rb:198:in `/^(?:|I )should be on (.+)$/'
features/tech_queue.feature:11:in `Then I should be on the dashboard page'