RailsApps / rails3-devise-rspec-cucumber

An example Rails 3.2 app with Devise and RSpec and Cucumber.
http://railsapps.github.io/
444 stars 144 forks source link

Couldn't find User with id=sign_out (ActiveRecord::RecordNotFound) #3

Closed empimp closed 13 years ago

empimp commented 13 years ago

Thanks for a great tutorial!

After I finished going through it, I was still having rspec and cucumber issues. I've since tracked down and corrected most of my typos :-) Unfortunately I've run into two cucumber failures, that appear to me to be related, but I have run out of google (actually duckduckgo,com, google, & stackoverflow) troubleshooting mojo. Any troubleshooting guidance would be very much appreciated.

The first error impacts the following three scenarios:

While the second impacts the, "User signs up with valid data" scenario.

My own troubleshooting has led me to believe that the problem is actually sign_out functionality, specifically as it relates to testing. One reason I believe this is the commonality between the errors: Couldn't find User with id=sign_out (ActiveRecord::RecordNotFound) ./app/controllers/users_controller.rb:5:inshow' (eval):2:in click_button' The other reason is that I was able to manually replicate these errors until I inserted the missing :method => 'delete' statement on the 'Logout' function of app/views/devise/menu/_login_items.html.erb.

I modified my sign_up.feature, but the other feature and step_definition files are unmodified copy/pastes from your repository.

The cucumber error messages, as well as the sign_up.feature, user_controller.rb, and web_steps.rb are available at https://gist.github.com/1066257.

Thanks, in advance, for any help troubleshooting this problem!

fortuity commented 13 years ago

Is your issue the same as this one reported on StackOverflow?

Rails 3.0.9 + Devise + Cucumber + Capybara the infamous “No route matches /users/sign_out”

empimp commented 13 years ago

@fortuity - I didn't replace prototype with jquery, so I didn't make the first change listed in Zeeshan's question, but I did try every subsequent change he made in both his question and answer, and still get the same errors.

If I understand most of what he's sayng (which is a big 'If') then his problem is tied directly to his use of jquery (e.g., he states "jquery_ujs.js has following method to convert the links with data-method="delete" attribute to a form and submit at runtime:")

And his cucumber failures were on the actual sign_out steps. My cucumber failures are are on four different steps, only one of which is a sign_out step, but for each of those failures the cucumber failure message includes the following:

Couldn't find User with id=sign_out (ActiveRecord::RecordNotFound)
./app/controllers/users_controller.rb:5:in `show'
(eval):2:in `click_button' 

I've uploaded my app to https://github.com/empimp/swabbie_pre-alpha/tree/feature/auth_and_auth

cailinanne commented 13 years ago

I'm having the same problem. I have made no modifications to the start app. Just created it with

rails new demo -m https://github.com/RailsApps/rails3-application-templates/raw/master/rails3-devise-rspec-cucumber-template.rb -T
cailinanne commented 13 years ago

Okay, I've spent some time investigating this.

The fundamental issue is that the application code provided just doesn't jive with the routes that are generated by Devise. This is a gem version issue. Back in Devise 1.3.2 a GET route was provided for destroy_user_session_path, but now only a DELETE route is provided. I have no idea when that changed, but that's the core issue here.

The easiest way to "get this working" is to force the routes to match the application. You can do this by modifying the routes file.

Remove:

devise_for :users

Add:

devise_for :users do
  get "/users/sign_out" => "devise/sessions#destroy", :as => :destroy_user_session
end

This is a bit dirty. I'm sure that Devise deprecated the GET route for good reason. However, fixing it any other way is beyond my Cucumber knowledge at this point, as every test in that suite ultimately relies on visit('/users/logout') which just isn't possible with the out-of-the-box Devise routes.

fortuity commented 13 years ago

@empimp and @cailinanne thanks for investigating and reporting the issue. I'm going to wait to take any action until I see if there is any change to Devise or Cucumber to accommodate this problem.

empimp commented 13 years ago

@cailinanne - Thank you for providing a work-around, getting me to a green cucumber, and restoring my newb sanity!

@fortuity and @cailinanne - Since it does seem like a Devise version issue, and I didn't see any reference of it in the Devise documentation or the issue log I opened a new issue and posted a linkback to this thread - https://github.com/plataformatec/devise/issues/1192 .

eidosabi commented 13 years ago

josevalim responded that "The default in a newly generated app is to only work with a DELETE. But there is a configuration option to change that in config/initializers/devise.rb" (over in https://github.com/plataformatec/devise/issues/1192#issuecomment-1536551).

Using the routes.rb in the tutorial AND commenting out config.sign_out_via = :delete in /config/initializers/devise.rb gives me a green cuc. :-) I've updated the tutorial accordingly.

fortuity commented 13 years ago

josevalim changed the default behavior on 27 June 2011 for Devise version 1.4.1. Here's the commit:

https://github.com/plataformatec/devise/commit/adb127bb3e3b334cba903db2c21710e8c41c2b40

He explains:

"GET requests should not change the state of the server. When sign out is a GET request, CSRF can be used to sign you out automatically and things that preload links can eventually sign you out by mistake as well."

eidosabi commented 13 years ago

I updated the wiki tutorial to read:

In /config/initializers/devise.rb you will also need to comment out @config.sign_out_via = :delete@:

# The default HTTP method used to sign out a resource. Default is :delete.
# config.sign_out_via = :delete

According to josevalim's comment this isn't the best way to handle this, and @cailinanne said her method was "a bit dirty", so what is the recommended way to use the default Devise sign_out?

fortuity commented 13 years ago

Cucumber wants to test GET requests not DELETE requests for destroy_user_session_path. If you intend to use Cucumber with Devise, change the Devise default from DELETE to GET for the Rails test environment only with this change to config/initializers/devise.rb:

config.sign_out_via = Rails.env.test? ? :get : :delete

Don't try to tweak the routes.rb file to make the fix. It isn't necessary.

Since you only use Cucumber during testing, switching the default is only needed for testing.

If you're not going to use Cucumber, leave Devise's new default (DELETE) in place.

I haven't raised this issue with the Cucumber experts (I'm curious what they will suggest). You may want to discuss it with them. If you gather more information, please bring it back here.

I've updated the application template and the Rails Apps Composer gem to detect the collision between Devise and Cucumber and the template will make the change to the the Devise initializer when Cucumber is included. The example source code now implements the change. I will review and update the tutorial as well.

Thank you, everyone, for contributing to resolution of this issue.

doolin commented 13 years ago

I found this same problem and more or less the same fix independently last week. It's definitely real. I haven't quite figured out which list (cucumber or devise) to describe too.

schadenfred commented 13 years ago

It seems like this is fundamentally a problem with Capybara, and that Capybara should be forced to accept the :delete method. Has anyone tried this?

ches commented 13 years ago

@fortuity I commented on the Stack Overflow thread you referenced above, but I think you should update the tutorial (and Devise wiki page on the topic!) -- sidestepping the changes that were made to Devise is not a good solution!

The quick answer is to make the "sign out" step definition use click_link so that cucumber-rails' magical JS emulation for Capybara steps in to handle things. The better but more involved answer I think is to use button_to for the logout as the OP arrived at on the SO thread. There is discussion relevant to both on this old Capybara issue.

DanielKehoe commented 13 years ago

@ches, thanks for suggesting click_link and providing the additional info.

ches commented 13 years ago

Welcome, thanks for the detailed tutorials :-)

stanley71 commented 12 years ago

Had this issue as well. Turned out to be an issue with my guard was running my tests in development environment instead of test. This is why the check in the devise.rb initalizer didn't work for me. Took me quite some time to discover this one! Maybe some newbie like me is facing this problem as well, so this could help. Found my solution thanks to this post: https://github.com/guard/guard-rspec/issues/93

evedovelli commented 11 years ago

The right way to use Capybara with Devise is explained in devise's wiki: https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara

Once you have included on your user_step.rb file:

include Warden::Test::Helpers
Warden.test_mode!

You may replace visit '/users/sign_out' with logout(:user)

ruby4life commented 9 years ago

I had the same issue

I noticed that I had java script disabled on my browser!

I enabled JS on my browser

And all is well