In teaching myself Ruby and Rails, I'm currently building a user system based closely on the Hartl tutorial; the main difference at this point is that I'm testing it with Rspec/Capybara rather than TestUnit. So far most things have been fairly straightforward, but I've been stuck on this one problem in testing password resets.
When I test password resets manually, they work exactly as expected. Hence I'm pretty sure the problem is somewhere between Rspec, Capybara, and FactoryGirl.
The spec code that keeps failing is when I want to visit the password forgotten link:
# password_resets_spec.rb
visit(edit_password_reset_path(spec_user.reset_token, email: spec_user.email))
It gives me:
ActionController::UrlGenerationError: No route matches {:action=>"edit", :controller=>"password_resets", :email=>"[email protected]", :format=>nil, :id=>nil} missing required keys: [:id]
Password resets are a (partial) resource, and their id is the user's reset_token.
spec_user is generated by FactoryGirl:
#password_resets_spec.rb
let(:spec_user){ FactoryGirl.create :user }
The relevant :user factory doesn't set reset_token but when I try it with one that does, the token does not get properly set by the reset form, and doesn't match the generated digest. Here is the code that sets both:
# user.rb
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
I can bypass the above error by using a factory that does set reset_token, but then the token used for the route doesn't match the digest saved, and the next step in the test fails.
My conclusion so far is that I'm misunderstanding something about how virtual attributes are handled, as the token is virtual while the digest is saved to database. I suspect that the reset_token I get for my route in the spec is not the same one involved in generating the digest, somehow.
Any help understanding what's going here would be much appreciated! :)