Reputation: 12739
I've created a learning application using Bort, which is a base app that includes Restful Authentication and RSpec. I've got it up and running and added a new object that requires users to be logged in before they can do anything(before_filter :login_required
in the controller). [edit: I should also mention that the user has_many
of the new class and only the user should be able to see it.]
I've created the new model/controller using Rspec's generators which have created a number of default tests. They all pass if there is no before_filter
but several fail, as should be expected, once the before_filter
is in place.
How do I get the generated tests to run as if there is/is not a logged in user? Do I need a whole batch of matching not logged in - redirect tests? I assume it is some sort of mocking or fixture technique but I am new to RSpec and a bit adrift. Good RSpec tutorial links would also be appreciated.
Upvotes: 17
Views: 12221
Reputation: 1697
When not testing the authentication but testing the controllers that needs the user to be authenticated I usually stub the filter method:
before(:each) do
controller.stub!(:authenticate).and_return(true)
end
The above example works where my before_filter is set to the authenticate method:
before_filter :authenticate
and the authenticate in my app uses HTTP Basic Authentication, but it really can be any other authentication mechanism.
private
def authenticate
authenticate_or_request_with_http_basic do |user,password|
user == USER_NAME && password == PASSWORD
end
end
I think it's a pretty straightforward way to test.
Upvotes: 7
Reputation: 15199
To mock a user being logged in, I hack into the controller to set @current_user
manually:
module AuthHelper
protected
def login_as(model, id_or_attributes = {})
attributes = id_or_attributes.is_a?(Fixnum) ? {:id => id} : id_or_attributes
@current_user = stub_model(model, attributes)
target = controller rescue template
target.instance_variable_set '@current_user', @current_user
if block_given?
yield
target.instance_variable_set '@current_user', nil
end
return @current_user
end
def login_as_user(id_or_attributes = {}, &block)
login_as(User, id_or_attributes, &block)
end
end
Upvotes: 1
Reputation: 12739
I found a few answers to my own question. Basically, I needed to understand how to mock out the user from restful_authentication
so that the autogenerated rspec controller tests could pass even though I had added before_filter: login_required
.
Here are a few of my just found resources:
rspec, restful_authentication, and login_required
using restful_authentication current_user inside controller specs
DRYing up your CRUD controller RSpecs
Upvotes: 4
Reputation: 16407
I have a very similar setup, and below is the code I'm currently using to test this stuff. In each of the describe
s I put in:
it_should_behave_like "login-required object"
def attempt_access; do_post; end
If all you need is a login, or
it_should_behave_like "ownership-required object"
def login_as_object_owner; login_as @product.user; end
def attempt_access; do_put; end
def successful_ownership_access
response.should redirect_to(product_url(@product))
end
If you need ownership. Obviously, the helper methods change (very little) with each turn, but this does most of the work for you. This is in my spec_helper.rb
shared_examples_for "login-required object" do
it "should not be able to access this without logging in" do
attempt_access
response.should_not be_success
respond_to do |format|
format.html { redirect_to(login_url) }
format.xml { response.status_code.should == 401 }
end
end
end
shared_examples_for "ownership-required object" do
it_should_behave_like "login-required object"
it "should not be able to access this without owning it" do
attempt_access
response.should_not be_success
respond_to do |format|
format.html { response.should be_redirect }
format.xml { response.status_code.should == 401 }
end
end
it "should be able to access this if you own it" do
login_as_object_owner
attempt_access
if respond_to?(:successful_ownership_access)
successful_ownership_access
else
response.should be_success
end
end
end
Upvotes: 7