Keeping user in the database between tests using Capybara? - ruby-on-rails

My issue is that I have to create a new user and login for each individual capybara test.
An example is below:
require 'spec_helper'
describe "users" do
describe "user registration" do
it "should create a new user and log in" do
# Register a new user to be used during the testing process
visit signup_path
fill_in 'Email', with: 'testuser'
fill_in 'Password', with: 'testpass'
fill_in 'Password confirmation', with: 'testpass'
click_button 'Create User'
current_path.should == root_path
page.should have_content 'Thank you for signing up!'
end
end
describe "user login" do
it "should log in" do
# log in
visit login_path
fill_in 'Email', with: 'testuser'
fill_in 'Password', with: 'testpass'
click_button 'Log In'
current_path.should == root_path
page.should have_content 'Logged in!'
end
end
end
The login test fails because the user no longer exists in the database for that test.
This could be fixed simply by putting both in one test, but I believe that is bad practice.
Also I have another file which currently is registering and logging in between each test using a before_do, which also seems to be quite bad... you can see that code here.
For the record this is my first rails app so perhaps I am trying to do this the wrong way. I would like to dry it up as much as possible..
Is capybara really this bad to use on pages that require user login?

I have done it this way.
require "spec_helper"
describe "Users" do
subject { page }
describe "User Registration" do
before { visit signup_path }
let(:submit) { "Sign up" }
describe "with invalid information" do
it "should not create a user" do
expect { click_button submit }.not_to change(User, :count)
end
end
describe "with valid information" do
before do
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar12"
fill_in "Password confirmation", with: "foobar12"
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after registration" do
before { click_button submit }
it { should have_content 'Thank you for signing up!' }
end
describe "after registration signout and login" do
let(:user) { User.find_by_email('user#example.com') }
before do
click_button submit
visit signout_path
sign_in user # sign_in is a method which u can define in your spec/support/utilities.rb . Define once and use at multiple places.
end
it { should have_content 'Logged In!' }
it { should have_link('Logout') }
end
end
end
end
# spec/support/utilities.rb
def sign_in(user)
visit sign_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Log in"
end
your every describe and it block will run after the before block in parent that's why we need to click_button in every block in above test cases.

Related

Id path on capybara

I have a problem.
I'm trying to use capybara to do integration tests, but I can not get the id on my page for it to make the visit when I test the context "edit new tarefa". I'm using devise, so I create the user at the beginning of the code.
Below is the code:
require 'rails_helper'
describe "Tarefas", :type => :feature do
feature "New Tarefa" do
background do
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
end
context "create new tarefa" do
it "preenchendo os campos" do
visit '/tarefas/new'
within("#new_tarefa") do
fill_in 'tarefa_titulo', with: 'user#example.com'
fill_in 'tarefa_descricao', with: 'password'
fill_in 'tarefa_data', with: '18/06/1990 20:00'
end
click_button 'submit'
expect(page).to have_content 'Mostra a tarefa selecionada'
end
end
context "edit new tarefa" do
it "alterando os campos" do
visit "tarefas/#{Tarefa.last.id}/edit"
within("#new_tarefa") do
fill_in 'tarefa_titulo', with: 'user#exa12mple.com'
fill_in 'tarefa_descricao', with: 'passw213ord'
fill_in 'tarefa_data', with: '18/06/1990 21:00'
end
click_button 'submit'
expect(page).to have_content 'Mostra a tarefa selecionada'
end
end
end
end
You should not rely on the tarefa you have created in "create new tarefa" to be available in you next spec as:
It won't work since the ordering is random.
It would create a coupling where one spec relies on the outcome of another.
Instead you want to use database_cleaner to clean out the DB after each spec and use let and let! to setup the requirements for each spec:
require 'rails_helper'
# You can use `feature` as a top level block.
# No need to nest it in descibe.
RSpec.feature "New Tarefa" do
let(:user) { FactoryGirl.create(:user) }
let(:tarefa) { FactoryGirl.create(:tarefa) }
background do
login_as(user, scope: :user)
end
context "create new tarefa" do
it "preenchendo os campos" do
visit '/tarefas/new'
within("#new_tarefa") do
fill_in 'tarefa_titulo', with: 'user#example.com'
fill_in 'tarefa_descricao', with: 'password'
fill_in 'tarefa_data', with: '18/06/1990 20:00'
end
click_button 'submit'
expect(page).to have_content 'Mostra a tarefa selecionada'
end
end
context "edit new tarefa" do
it "alterando os campos" do
# tarefa is created when you reference it.
visit "tarefas/#{tarefa.to_param}/edit"
within("#new_tarefa") do
fill_in 'tarefa_titulo', with: 'user#exa12mple.com'
fill_in 'tarefa_descricao', with: 'passw213ord'
fill_in 'tarefa_data', with: '18/06/1990 21:00'
end
click_button 'submit'
expect(page).to have_content 'Mostra a tarefa selecionada'
end
end
end

How to factor Capybara rspec testing code?

I need to test a system in which everything is available only after a user is signed in using Devise. Every time I use "it" I have to include the signup code.
Is there a way to factor the code below so that the "let's me make a new post" test and similar tests won't have to include the sign up?
describe "new post process" do
before :all do
#user = FactoryGirl.create(:user)
#post = FactoryGirl.create(:post)
end
it "signs me in" do
visit '/users/sign_in'
within(".new_user") do
fill_in 'Email', :with => 'user#example.com'
fill_in 'Password', :with => 'password'
end
click_button 'Log in'
expect(page).to have_content 'Signed in successfully'
end
it "let's me make a new post" do
visit '/users/sign_in'
within(".new_user") do
fill_in 'Email', :with => 'user#example.com'
fill_in 'Password', :with => 'password'
end
click_button 'Log in'
visit '/posts/new'
expect( find(:css, 'select#post_id').value ).to eq('1')
end
end
Your first option is to use the Warden methods provided, as per the documentation on this page:
https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
Your second option is just to login for real in your tests as you have done in your examples. You can streamline this though by creating some helper methods to do the login work rather than duplicating the code in all of your tests.
To do this, I would create a support directory within your spec directory, and then a macros directory within that. Then create a file spec/support/macros/authentication_macros.rb:
module AuthenticationMacros
def login_as(user)
visit '/users/sign_in'
within('.new_user') do
fill_in 'Email', with: user.email
fill_in 'Password', with: user.password
end
click_button 'Log in'
end
end
Next, update your RSpec config to load your macros. In either spec_helper.rb or rails_helper.rb if you're using a newer setup:
# Load your support files
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Include the functions defined in your modules so RSpec can access them
RSpec.configure do |config|
config.include(AuthenticationMacros)
end
Finally, update your tests to use your login_as function:
describe "new post process" do
before :each do
#user = FactoryGirl.create(:user)
#post = FactoryGirl.create(:post)
login_as #user
end
it "signs me in" do
expect(page).to have_content 'Signed in successfully'
end
it "let's me make a new post" do
expect( find(:css, 'select#post_id').value ).to eq('1')
end
end
Obviously, make sure you have password defined in your user factory.

Undefined method "Get" and "Let" with Rspec 3 - rails 4

I've decided to start a new project using the new Rspec 3 (+ capybara/factory_girl) and am having trouble learning the new syntax. Right now I have
user_pages_spec.rb (Feature)
scenario "Signing Up" do
let(:submit) { "Sign up" }
scenario "With valid information" do
background do
fill_in "Username", with: "example"
fill_in "Email", with: "example#example.com"
fill_in "Password", with: "foobar123"
fill_in "Password confirmation", with: "foobar123"
end
scenario "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end
Fails with undefined method 'let'. And:
static_pages_spec.rb (controller)
describe StaticPagesController do
describe "GET 'home'" do
it "returns http success" do
get :home
expect(response).to be_success
end
end
end
with "undefined method 'get'. (This is just the default controller spec)
When upgrading existing project from RSpec 2.x to 3.0 had same problem.
It was fixed for me with an explicit setting of the type.
Could you try this:
describe StaticPagesController, type: :controller do
EDIT:
I found now that the more structural cause and solution is that
in RSpec 3, I needed to add:
config.infer_spec_type_from_file_location!
in the config block in spec_helper.rb
You are getting undefined method let because capybara defines scenario an alias of it and feature as alias of describe. However, let is available in an example group context (a describe or context block) but not an individual example (and it block). So your example is equivalent to:
it "Signing Up" do
let(:submit) { "Sign up" }
it "With valid information" do
background do
fill_in "Username", with: "example"
fill_in "Email", with: "example#example.com"
fill_in "Password", with: "foobar123"
fill_in "Password confirmation", with: "foobar123"
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end
...but should be:
feature "Signing Up" do
let(:submit) { "Sign up" }
context "With valid information" do
background do
fill_in "Username", with: "example"
fill_in "Email", with: "example#example.com"
fill_in "Password", with: "foobar123"
fill_in "Password confirmation", with: "foobar123"
end
scenario "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end
Or, if you want to stick with pure RSpec constructs (rather than the capybara aliases):
describe "Signing Up" do
let(:submit) { "Sign up" }
context "With valid information" do
before do
fill_in "Username", with: "example"
fill_in "Email", with: "example#example.com"
fill_in "Password", with: "foobar123"
fill_in "Password confirmation", with: "foobar123"
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end

Correct way to write rspec/capybara test?

I'm new to Ruby and Rails and I'm trying to implement tests the correct way. I'm currently working on having users sign up. I would like the first user to sign up to be an admin and then every user after that to be a regular user.
I'm currently thinking I need to write my test as a feature but I'm wondering if this should actually be a model test.
My current code is right here
require 'spec_helper'
describe "User pages" do
subject { page }
describe "Sign up page" do
before { visit signup_path }
it { should have_button('Create!') }
end
describe "Creating an account" do
before { visit signup_path }
let(:submit) { "Create!" }
describe "with invalid information" do
it "should not create the user account" do
expect { click_button "Create!"}.not_to change(User, :count)
end
describe "it should display errors" do
before { click_button submit }
it { should have_content('Failed signup') }
end
end
describe "with valid information" do
before do
fill_in "Name", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "password"
fill_in "Confirm Password", with: "password"
end
it "should create the user" do
expect { click_button submit }.to change(User, :count).by(1)
end
# User sign in not implemented at this point
describe "the first user" do
User.all.count.should == 0
click_button submit
User.all.count.should == 1
#firstUser = User.first
#firstUser.is_admin?.should == true
describe "the second user" do
before do
visit signup_path
fill_in "Name", with: "Example User2"
fill_in "Email", with: "user2#example.com"
fill_in "Password", with: "password"
fill_in "Confirm Password", with: "password"
end
click_button submit
#secondUser = User.all.last
#secondUser.is_admin?.should == false
end
end
end
end
end
I'm mostly concerned with the "with valid information" part and would like to clean that up so it fits in with rspec/capybara correctly.
you can try this great tutorial by michael hartl , he use Rspec in this tutorial you can learn from it :)
i am a beginner too and i started with it :D

Rails Tutorial: RSpec test decoupling

I'm trying to do Exercise 2 of Chapter 8.5 in Michael Hartl's Ruby on Rails Tutorial. The exercise is as follows:
Following the example in Section 8.3.3, go through the user and authentication request specs (i.e., the files currently in the spec/requests directory) and define utility functions in spec/support/utilities.rb to decouple the tests from the implementation. Extra credit: Organize the support code into separate files and modules, and get everything to work by including the modules properly in the spec helper file.
Example 8.3.3: utilities.rb
include ApplicationHelper
def valid_signin(user)
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
end
RSpec::Matchers.define :have_error_message do |message|
match do |page|
page.should have_selector('div.alert.alert-error', text: message)
end
end
The defined valid_signin(user) function is used in the following block of authentication_pages_spec.rb and works fine.
describe "with valid information" do
let(:user){FactoryGirl.create(:user)}
before { valid_signin(user) }
it { should have_selector('title', text: user.name) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
it { should_not have_link('Sign in', href: signin_path) }
describe "followed by signout" do
before { click_link "Sign out" }
it { should have_link('Sign in') }
end
end
So with this example I set about to create my own named valid_signup(user):
def valid_signup(user)
fill_in "Name", with: user.name
fill_in "Email", with: user.email
fill_in "Password", with: user.password
fill_in "Confirmation", with: user.password_confirmation
end
I'm using this block in user_pages_spec.rb like this:
describe "with valid information" do
let(:user){FactoryGirl.create(:user)}
before { valid_signup(user) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
let(:user) { User.find_by_email(user.email) }
it { should have_selector('title', text: user.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
It doesn't work. Spork/Guard reports these errors:
Failures:
1) UserPages signup with valid information should create a user
Failure/Error: expect { click_button submit }.to change(User, :count).by(1)
count should have been changed by 1, but was changed by 0
# ./spec/requests/user_pages_spec.rb:46:in `block (4 levels) in '
2) UserPages signup with valid information after saving the user
Failure/Error: before { valid_signup(user) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in '
3) UserPages signup with valid information after saving the user
Failure/Error: before { valid_signup(user) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in '
4) UserPages signup with valid information after saving the user
Failure/Error: before { valid_signup(user) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./spec/support/utilities.rb:10:in `valid_signup'
# ./spec/requests/user_pages_spec.rb:43:in `block (4 levels) in '
The errors seem to suggest the user.name in my valid_signup(user) function in utilities.rb isn't defined, but I don't see any reason why. I've restarted Guard several times, and did a rake db:test:prepare to make sure the testing db (using postgresql) was in order.
Here's my factories.rb for completeness:
FactoryGirl.define do
factory :user do
name "Example User"
email "user#example.com"
password "foobar"
password_confirmation "foobar"
end
end
Before I continue to try and decouple more of the testing suite I'd very much like to solve this error and, more importantly, understand the reason for it.
EDIT
I've tried your tips, and edited the function in user_pages_spec.rb as follows:
describe "with valid information" do
before { valid_signup(user) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
let(:user) { User.find_by_email('user#example.com') }
it { should have_selector('title', text: user.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
Since I removed let(:user){FactoryGirl.create(:user)} from the function I guessed there was no longer a user created in the function so I needed to define valid_signup(user) as such as the user variable for valid_signup was no longer being filled by FactoryGirl:
def valid_signup(user)
fill_in "Name", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
This didn't work and gave me the following errors:
Failures:
1) UserPages signup with valid information should create a user
Failure/Error: before { valid_signup(user) }
NameError:
undefined local variable or method user' for #<RSpec::Core::ExampleGroup::Nested_5::Nested_3::Nested_2:0x007fdafc5088c0>
# ./spec/requests/user_pages_spec.rb:42:inblock (4 levels) in '
2) UserPages signup with valid information after saving the user
Failure/Error: it { should have_selector('title', text: user.name) }
NoMethodError:
undefined method name' for nil:NilClass
# ./spec/requests/user_pages_spec.rb:52:inblock (5 levels) in '
I also tried running the test with valid_signup(user) the way I used to have it before (with user.name, user.email, user.password, user.password_confirmation, which didn't work either, with errors:
Failures:
1) UserPages signup with valid information should create a user
Failure/Error: before { valid_signup(user) }
NameError:
undefined local variable or method `user' for #
# ./spec/requests/user_pages_spec.rb:42:in `block (4 levels) in '
2) UserPages signup with valid information after saving the user
Failure/Error: it { should have_selector('title', text: user.name) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./spec/requests/user_pages_spec.rb:52:in `block (5 levels) in '
Next I tried running it without passing variables in user_pages_spec.rb: before { valid_signup() } and without a variable in the function in utilities.rb:
def valid_signup()
fill_in "Name", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
This returned:
Failures:
1) UserPages signup with valid information should create a user
Failure/Error: before { valid_signup(user) }
NameError:
undefined local variable or method `user' for #
# ./spec/requests/user_pages_spec.rb:42:in `block (4 levels) in '
2) UserPages signup with valid information after saving the user
Failure/Error: it { should have_selector('title', text: user.name) }
NoMethodError:
undefined method `name' for nil:NilClass
# ./spec/requests/user_pages_spec.rb:52:in `block (5 levels) in '
Still no closer to the answer. I might be overlooking something simple. No clue what though. I got what I first did wrong though: I just thought FactoryGirl was a way to create variables, and I didn't know it actually did something to my test database.
I will try to explain what is going on in your original test (which I find easier to fix than the edited version):
describe "with valid information" do
let(:user) {FactoryGirl.build(:user)} # FactoryGirl.create will save the instance, you should be using build instead
before { valid_signup(user) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
# let(:user) { User.find_by_email(user.email) } # this is not needed any more
it { should have_selector('title', text: user.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
More info on FactoryGirl usage: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#using-factories
FactoryGirl saves the user to the database, then you visit the sign_in_path with the user already on the database and fill the form for sign_in with valid_sigin(user)
let(:user){FactoryGirl.create(:user)}
before { valid_signin(user) }
When you do:
let(:user){FactoryGirl.create(:user)}
before { valid_signup(user) }
factory girl saves the user in the database, and you fill a form with an email already taken.
EDIT:
describe "with valid information" do
before { valid_signup(user) }
You dont have a variable user defined, since you deleted let(:user){FactoryGilr.create(:user)},and you should visit the right path, your current path is "sign_in_path" and should be "sign_up_path"
You should do something like this:
utilities.rb
def valid_sign_up(user)
fill_in "Name", with: user.name
fill_in "Email", with: user.email
fill_in "Password", with: user.password
fill_in "Confirmation", with: user.password_confirmation
end
user_pages_spec.rb
describe "with valid information" do
let(:user){User.new(name: "my name", email: "myemail#example"...)
before do
visit sign_up
valid_sign_up(user)
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
I had the same problem and figured out the solution: when you define valid_signup, it should take 'page' as the argument. After all, you are testing the page elements, not the user.
spec/support/utilities.rb
def valid_signup(page)
fill_in "Name", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
spec/requests/user_pages_spec.rb
describe "with valid information" do
before { valid_signup(page) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
I hope this helps!
UPDATE I realize now that this works because of the scope of the variable 'page' (since it's the subject). To use "user" I added the line
let(:user) { FactoryGirl.create(:user) }
above
before { sign_up(user) }. This then broke a later spec where I also tried using 'user' as a variable, so I changed the name to 'editeduser'. Here's the full example:
user_pages_spec.rb
require 'spec_helper'
describe "UserPages" do
subject { page }
...
describe "signup page" do
before { visit signup_path }
let(:submit) { "Create my account" }
it { should have_selector('h1', text: 'Sign up') }
it { should have_selector('title', text: full_title('Sign up')) }
describe "with invalid information" do
it "should not create a user" do
expect { click_button submit }.not_to change(User, :count)
end
describe "after submission" do
before { click_button submit }
it { should have_selector('title', text: 'Sign up') }
it { should have_content('error') }
end
end
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before { sign_up(user) }
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
let(:editeduser) { User.find_by_email('user#example.com') }
it { should have_selector('title', text: editeduser.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
end
Hopefully this helps someone!
I was curious about this one as well, and found an answer that may be more in line with what Hartl was expecting (though as just learning, I'm not 100% certain the top answer isn't more elegant or not).
Since we weren't using FactoryGirl to sign up users, but instead to sign them in, I didn't want to use it in my refactoring. This is what I have in my utilities.rb:
def valid_signup
fill_in "Name", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
and then in user_pages_spec.rb I replaced
describe "with valid information" do
before do
fill_in "Name", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
with
describe "with valid information" do
before { valid_signup }
We don't need a user to be saved to the database just to check a one time sign up, since they don't need to persist through multiple page views. Also, since we don't look up a user, we don't need a (user) argument after valid_signup method (I think I have the terminology correct. Please correct me if I do not.)

Resources