I've searched around and this seems like a common enough issue, but nothing has worked for me. Here goes!
Firstly, I'm working through the Hartl RoR tutorial. I'm towards the end of section 9 (video 905) and I'm trying to test a failing user login. Here's the code in user_spec.rb:
describe "failure" do
it "should not log the user in" do
visit login_path
puts response.body
fill_in 'session_email', with: ""
fill_in 'session_password', with: ""
click_button
response.should have_selector('div.login_error', content: "Invalid")
response.should render_template('sessions/new')
end
end
and here's the autotest error:
1) User login failure should not log the user in
Failure/Error: visit login_path
NameError:
undefined local variable or method `login_path' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_5::Nested_1:0x00000102c79248>
# ./spec/models/user_spec.rb:176:in `block (4 levels) in <top (required)>'
I tried writing visit login_path as visit '/login', and still got an error, although it changed to this:
Failure/Error: visit '/login'
NoMethodError:
undefined method `visit' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_5::Nested_1:0x00000102c2d140>
Which makes me think this is maybe a webrat issue, as I read somewhere that 'visit' comes from webrat (and not rspec). I also know that it's not getting past the first line, as the puts response.body isn't showing up on the autotest failure. More confusing is that in another part of my app, I have other test code that's virtually identical and it works fine.
layout_links_spec.rb:
before(:each) do
#user = Factory(:user)
visit login_path
# puts response.body
fill_in 'session_email', with: #user.email
fill_in 'session_password', with: #user.password
click_button
end
Any ideas?
I had to do a lot of updates to get your code running. I haven't done Rails on my home machine for a while so it was good to get it all up to date.
Found a solution to getting named routes to work but that just led to another problem. Finally the light went on. You are doing the test in the wrong place. The user model spec is not where you test login. As you mentioned, another similar test works. That is in the Request specs. The visit command and named routes are designed to work in that directory.
Related
I am experiencing strange very test behavior, with logged in state being handled inconsistently.
The spec logs a user in, visits a (nested or un-nested) index page, and checks that the correct content is displayed. Records are fetched asynchronously, though I don't think this should have an impact.
When each spec is run individually, they each pass. When all specs are run together, they fail because the expected content is missing. Using save_and_open_page reveals this is because the login page is being rendered, rather than the expected index page.
Why does rspec think the user is not signed in when all specs are run together, yet each spec passes individually?
The tests look something like this
let(:user) {create :user}
let(:team) {create :team}
let(:country) {create :country}
before :each do
login_as( user, scope: :user )
end
describe 'unnested' do
it 'should have the expected content', :js do
visit users_path
is_expected.to have_content "some content on the page"
end
end
describe 'nested by team' do
it 'should have the expected content', :js do
visit team_users_path(team)
is_expected.to have_content "some content on the page"
end
end
describe 'nested by nationality' do
it 'should have the expected content', :js do
visit country_users_path(country)
is_expected.to have_content "some content on the page"
end
end
The specs all require javascript (I don't know whether that is important here).
Authentication is handled by Devise, and my rails_helper.rb includes
config.append_after(:each) do
DatabaseCleaner.clean
Warden.test_reset!
end
Why does rspec think the user is not signed in when all specs are run together, yet each spec passes individually?
It took a long time to get to the bottom of this. Posting this hear in case it is of help to anyone else encountering the same issue.
After much searching I eventually found this small mention that login_as may not work with Poltergeist when js is enabled on your test scenarios.
I tried the suggested fix to deal with shared DB connections. Unfortunately this resulted in the following errors:
PG::DuplicatePstatement at /session/users/signin
ERROR: prepared statement "a1" already exists
I tried using the Transactional Capybara gem, but this did not seem to work well with Poltergeist.
Eventually I abandonned login_as completely, and instead wrote a short method that visits the login page, fills in email and password, and logs in that way.
This solution appears to be working. It adds a little overhead, so I'm only using it for tests with JS.
If you are using Capybara gem then there is no need to use :js with test cases
What I did if this helps-
scenario "visit with user signed in" do
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
visit "/"
expect(current_path).to eq('/')
expect(page).to have_title "Some Random Title"
end
The other way you can login user using feature specs like-
feature 'User signs in' do
before :each do
#user = FactoryGirl.create(:user)
end
scenario "Signing in with correct credentials" do
visit "/"
fill_in "Email", with: #user.email
fill_in "Password", with: #user.password
click_button "Log In"
expect(current_path).to eq("/login/useremail/verification")
expect(page).to have_content "Signed in successfully"
end
end
If your pages are ajax then refer to this https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara
I am trying to test what happens after logging in through Devise gem. For example, I have the controller to go to student_dashboard_path after users successfully login.
How can I test this with Capybara and Rspec?
I currently have this in:
/spec/features/user_signs_in_sees_dashboard_spec.rb
require 'rails_helper'
feature 'User sign in' do
scenario 'successfully from sign in page and sees student dashboard' do
sign_in
visit student_dashboard_path
expect(current_path).to eq(student_dashboard_path)
end
end
and I have this in:
/spec/support/features/sign_in.rb
module Features
def sign_in
visit user_session_path
fill_in 'Email', with: User.first.email
fill_in 'Password', with: User.first.password
click_button 'Log in'
end
end
and I am getting this error message:
1) User signs in successfully from sign in and sees dashboard
Failure/Error: expect(current_path).to eq(student_dashboard_path)
expected: "/student/dashboard"
got: "/student/login"
I am not sure why I am not able to log in and see the student dashboard.
I'm leaving my original answer below for anyone still on Capybara < 2.5.0 but in 2.5.0 you can now do
expect(page).to have_current_path(<expected path>)
and it will use Capybara's waiting behavior while checking for the path
---- Below is only for Capybara version < 2.5.0
expect(current_path).to eq(...) doesn't wait for the path to change, it just compares to the current path at the time it's called. If you put a sleep after the click button I bet it works. A better solution would be to have something like
expect(page).to have_text('You are now logged in')
after the click_button. That would cause capybara to wait until the log in has completed, the page loads (and the logged in notice appears), and therefore until the current_path has changed too.
Use capybara save_and_open_page in middle to figure out if the fields are properly set. If you work on the same machine you can switch to selenium to test out on the real browser.
Also be sure that this code does not need any JS to work because default capybara matchers will not be able to run it.
I'm currently working through chapter seven of Rails Tutorial and seem to be stuck at figure 7.22. In brief, I'm unable to get the tests to pass. When I run . . .
bundle exec rspec spec/requests/user_pages_spec.rb
. . . I see a bunch of failed tests that read:
Failures:
1) User pages signup page
Failure/Error: before { visit signup_path }
NameError:
undefined local variable or method `signup_path' for # <RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x007fb2be028410>
# ./user_pages_spec.rb:9:in `block (3 levels) in <top (required)>'
2) User pages signup page
Failure/Error: before { visit signup_path }
NameError:
undefined local variable or method `signup_path' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x007fb2be048ad0>
# ./user_pages_spec.rb:9:in `block (3 levels) in <top (required)>'
3) User pages signup page with invalid information should not create a user
Failure/Error: before { visit signup_path }
NameError:
undefined local variable or method `signup_path' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1::Nested_1:0x007fb2be062e80>
# ./user_pages_spec.rb:9:in `block (3 levels) in <top (required)>'
4) User pages signup page with valid information should create a user
Failure/Error: before { visit signup_path }
NameError:
undefined local variable or method `signup_path' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1::Nested_2:0x007fb2be083158>
# ./user_pages_spec.rb:9:in `block (3 levels) in <top (required)>'
Finished in 0.00374 seconds
4 examples, 4 failures
Failed examples:
rspec ./user_pages_spec.rb:11 # User pages signup page
rspec ./user_pages_spec.rb:12 # User pages signup page
rspec ./user_pages_spec.rb:18 # User pages signup page with invalid information should not create a user
rspec ./user_pages_spec.rb:32 # User pages signup page with valid information should create a user
Randomized with seed 10291
I'm guessing the main error involves the undefined method or varialble "signup_path," but I have no clue whatsoever where it's supposed to be defined or whether it should have at some point automatically been defined by Rails in the first place.
Could someone help me with this?
UPDATE: Below is my routes file.
Thanks for your reply. Here's my routes file. Unless I'm missing something, everything seems OK to me:
SecondSampleApp::Application.routes.draw do
get "users/new"
get "static_pages/home"
get "static_pages/help"
get "static_pages/about"
resources :users
root "static_pages#home"
match "/signup", to: "users#new", via: "get"
match "/help", to: "static_pages#help", via: "get"
match "/about", to: "static_pages#about", via: "get"
match "/contact", to: "static_pages#contact", via: "get"
end
spec/spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
RSpec.configure do |config|
config.include Capybara::DSL
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = true
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
config.include Rails.application.routes.url_helpers
end
Here are the failing tests in user_pages_spec.rb:
require 'spec_helper'
describe "User pages" do
subject { page }
describe "signup page" do
before { visit signup_path }
it { should have_content('Sign up') }
it { should have_title(full_title('Sign up')) }
let(:submit) { "Create my account" }
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 "Name", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end
describe "profile page" do
let(:user) { FactoryGirl.create(:user) }
before {visit user_path(user)}
it {should have_content(user.name)}
it {should have_title(user.name)}
end
end
When going through the Hartl tutorial and you encounter an undefined local variable or method error or otherwise want to find out what the current definition of a variable or method is supposed to be at that point in the tutorial, the following technique is helpful:
Go to the book on the web
Click the "view as single page" link in the upper right hand corner
With the browser's search command, search backwards from where you are in the tutorial for variable/method name in question. (Note: If there are lots of them, and you're sure it's a method, you can narrow the search to "def variable_or_method_in_question")
Note the definition and the file in which the definition occurs (typically given in the header of the table/listing/figure)
Going to the github repository is helpful sometimes as well, but that shows the final state/location of the code, which can often be misleading.
In your case, this technique results in finding an entry in Table 5.1 which indicates that this variable is a path generated by Rails as a result of the contents of your routes.rb file. You should check the contents of that file and look at the currently defined routes using rake routes on the command line.
This nolonger applies to the tutorial by Michael Hartl http://www.railstutorial.org/book
Spork is not being used in tutorial for Rails 4 now, this may change, I followed the page linked below but it will overwrite the existing spec setup and install Gems not required by the tutorial.
Are you using Spork? If so you have to restart it any time you make changes in routes.rb.
Described in Listing 3.38:
One word of advice when using Spork: after changing a file included in the prefork loading (such as routes.rb), you will have to restart the Spork server to load the new Rails environment. If your tests are failing when you think they should be passing, quit the Spork server with Ctrl-C and restart it.
http://ruby.railstutorial.org/chapters/static-pages#sec-spork
For Rails 5 add code to rails_helper.rb :
include Rails.application.routes.url_helpers
I'm going through RailsTutorial and I'm stuck at chapter 9.3. Rspec keeps giving me these errors when I try to run a suite test at the end of 9.3:
/Users/shaan/Sites/sample_app/spec/support/utilities.rb:15:in `sign_in': undefined local variable or method `signin_path' for #<Class:0x107759358> (NameError)
/Library/Ruby/Gems/1.8/gems/activerecord-3.2.3/lib/active_record/validations.rb:56:in `save!': Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)
First I got the top error, then ran the test again many times and I keep getting the second error.
Below are my controller routes and test files:
controller: http://pastebin.com/vJdCxthQ
test: http://pastebin.com/SEVLd9Gg
routes: http://pastebin.com/7LjA5Any
Utilities.rb:
include ApplicationHelper
def full_title(page_title)
base_title = "Ruby on Rails Tutorial Sample App"
if page_title.empty?
base_title
else
"#{base_title} | #{page_title}"
end
end
def sign_in(user)
visit signin_path
fill_in "Email", :with => user.email
fill_in "Password", :with => user.password
click_button "Sign in"
# Sign in when not using Capybara as well.
cookies[:remember_token] = user.remember_token
end
Any idea what I am doing wrong?
Check this line
def sign_in(user)
visit signin_path
...
end
signin_path seems to be incorrect. run rake routes to get the correct path & replace that with signin_path
I spent a long time chasing this confusing error around too. In my case, the issue was a difference between Rails versions. Try renaming your 'spec/requests' directory to 'features'
I'm setting up some integration tests using capybara and rspec.
In a single test, this works:
describe "SIGN IN, POST post" do
it "redirects to /posts after creating a new post" do
visit new_artist_session_path
fill_in 'Email', :with => 'vargas#vargas.com'
fill_in 'Password', :with => 'password'
click_link_or_button 'artist_submit'
visit "/artists/vargas/posts"
page.should have_content("Upload")
click_button 'Upload'
URI.parse(current_url).path.should == "/artists/vargas/posts"
end
end
However, I want to move the "sign_in" portion to a before(:all) filter block so that I can DRY up my tests. However it seems that within the before(:all) block, the same code gives this error:
Failure/Error: visit new_artist_session_path
NameError:
undefined local variable or method `new_artist_session_path' for #<RSpec::Core::ExampleGroup::Nested_1:0x0000010399e388>
It seems that the routes url helpers are not available from within the before block? How do I remedy this?
it appears that the paths helpers are available only between the "it" and "do". Just not in the before(:all) method. So I ended up just creating a method for signing in and included it in each test that needed it.