I've been learning Rails 3 with Devise and, so far, seem to have it working quite well. I've got custom session & registration controllers, recaptcha is working and a signed-in user can upload an avatar via carrierwave, which is saved on S3. Pretty happy with my progress.
Now I'm writing Rspec tests. Not going so well! I have a reasonable User model test, but that's because I found it online (https://github.com/RailsApps/rails3-devise-rspec-cucumber/) and was able to add to it by following Michael Hartl's excellent "Ruby on Rails 3 Tutorial".
My real problem is controller test and integration tests, especially controller tests. Initially I thought I'd be able to convert the tests in Michael's book, and I have to a small degree, but it's slow progress and I seem to be constantly hitting my head against a brick wall - partly, I think, because I don't know Rspec and capybara so well (have made some very dumb mistakes) but also because I don't really understand Devise well enough and am wondering if Devise plays as nicely as it might with Rspec; I read somewhere that, because Devise is Rack based, it might not always work as one might expect with Rspec. Don't know if that's true or not?
I know some people will wonder why this might be necessary since Devise is a gem and therefore already tested but I've had a couple of instances where changes elsewhere have broken login or registration without me immediately realizing. I think a good set of controller & integration tests would have solved this.
If I was able to do this myself I would and I'd publish it for others but, so far, writing these tests has been extremely painful and I really need to move on to other things.
I'm sure I wouldn't be the only one who could use this. Anyone know of a such a suite of tests?
In response to Jesse's kind offer of help...
Here is my registrations_controller_spec. The comments in "should render the 'edit' page" show the sort of things I am struggling with. Also, "should create a user" has some things I've tried to test but not been able to:
require File.dirname(__FILE__) + '/../spec_helper'
describe Users::RegistrationsController do
include Devise::TestHelpers
fixtures :all
render_views
before(:each) do
@request.env["devise.mapping"] = Devise.mappings[:user]
end
describe "POST 'create'" do
describe "failure" do
before(:each) do
@attr = { :email => "", :password => "",
:password_confirmation => "", :display_name => "" }
end
it "should not create a user" do
lambda do
post :create, :user_registration => @attr
end.should_not change(User, :count)
end
it "should render the 'new' page" do
post :create, :user_registration => @attr
response.should render_template('new')
end
end
describe "success" do
before(:each) do
@attr = { :email => "user@example.com",
:password => "foobar01", :password_confirmation => "foobar01", :display_name => "New User" }
end
it "should create a user" do
lambda do
post :create, :user => @attr
response.should redirect_to(root_path)
#response.body.should have_selector('h1', :text => "Sample App")
#response.should have_css('h1', :text => "Sample App")
#flash[:success].should == "A message with a confirmation link has been sent to your email address. Please open the link to activate your account."
#response.should have_content "A message with a confirmation link has been sent to your email address. Please open the link to activate your account."
end.should change(User, :count).by(1)
end
end
end
describe "PUT 'update'" do
before(:each) do
@user = FactoryGirl.create(:user)
@user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
sign_in @user
end
describe "Failure" do
before(:each) do
# The following information is valid except for display_name which is too long (max 20 characters)
@attr = { :email => @user.email, :display_name => "Test", :current_password => @user.password }
end
it "should render the 'edit' page" do
put :update, :id => subject.current_user, :user => @attr
# HAVE PUT THE DEBUGS THAT I'D LIKE TO GET WORKING FIRST
# Would like to be able to debug and check I'm getting the error(s) I'm expecting
puts subject.current_user.errors.messages # doesn't show me the errors
# Would like to be able to debug what html is being returned:
puts page.html # only return the first line of html
# Would like to be able to determine that this test is failing for the right reasons
response.should have_content "Display name is too long (maximum is 20 characters)" # doesn't work
response.should render_template('edit')
end
end
describe "Success" do
it "should change the user's display name" do
@attr = { :email => @user.email, :display_name => "Test", :current_password => @user.password }
put :update, :id => subject.current_user, :user => @attr
subject.current_user.reload
response.should redirect_to(root_path)
subject.current_user.display_name == @attr[:display_name]
end
end
end
describe "authentication of edit/update pages" do
describe "for non-signed-in users" do
before(:each) do
@user = FactoryGirl.create(:user)
end
describe "for non-signed-in users" do
it "should deny access to 'edit'" do
get :edit, :id => @user
response.should redirect_to(new_user_session_path)
end
it "should deny access to 'update'" do
put :update, :id => @user, :user => {}
response.should redirect_to(new_user_session_path)
end
end
end
end
end
OK, so a couple of things to make this work:
Here's how you should test that the body have specific text (you were missing the .body
response.body.should include "Display name is too long (maximum is 30 characters)"
You could also test that the @user has an error:
assigns[:user].errors[:display_name].should include "is too long (maximum is 30 characters)"
But, you do need to actually make the name be over 20/30 characters long, so I changed the attributes being posted to:
@attr = { :email => @user.email, :display_name => ("t" * 35), :current_password => @user.password }