I have a hard time testing my controller with before_filters, exceptions and some mocking and stubing. Here is the controller:
before_filter :get_subject, :only => [:show, :edit, :update, :destroy, :update_field]
before_filter :user_has_to_belongs_to_subject_company, :only => [:show, :edit, :update, :destroy, :update_field]
def show
@me开发者_如何转开发ssages = @subject.get_user_messages(current_user)
end
private
def get_subject
@subject = Subject.find(params[:id])
end
def user_has_to_belongs_to_subject_company
unless @current_user.company.eql?(@subject.company)
raise "Error: current_user does not belongs to subject's company"
end
end
And here is my spec file:
require 'spec_helper'
describe SubjectsController do
describe "for signed users" do
before(:each) do
@current_user = Factory(:user)
sign_in @current_user
end
describe "for user belonging to subject's company" do
before(:each) do
@subject = mock_model(Subject)
Subject.stub!(:find).with(@subject).and_return(@subject)
@current_user.stub_chain(:company, :eql?).and_return(true)
@subject.stub!(:company)
end
it "should not raise an exception" do
expect { get :show, :id => @subject }.to_not raise_error
end
end
describe "for user not belonging to subject's company" do
before(:each) do
@subject = mock_model(Subject)
Subject.stub!(:find).with(@subject).and_return(@subject)
@current_user.stub_chain(:company, :eql?).and_return(false)
@subject.stub!(:company)
end
it "should raise an exception" do
expect { get :show, :id => @subject }.to raise_error
end
end
end
end
And finally, here is the error message:
SubjectsController for signed users for user belonging to subject's company should not raise an exception
Failure/Error: expect { get :show, :id => @subject }.to_not raise_error
expected no Exception, got #<RuntimeError: Error: current_user does not belongs to subject's company>
# ./spec/controllers/subjects_controller_spec.rb:19:in `block (4 levels) in <top (required)>'
Thx for helping!
I'm not seeing the problem, but here's a refactoring suggestion. If you find yourself using more mocks and stubs that usual, maybe it's time to reconsider your interfaces. In this case, you can make your controller skinnier and you model fatter.
# subjects_controller_spec.rb
describe "for user belonging to subject's company" do
before(:each) do
@subject = mock_model(Subject, :verify_user => true)
Subject.stub!(:find).with(@subject).and_return(@subject)
end
# subjects_controller.b
def user_has_to_belongs_to_subject_company
@subject.verify_user(@current_user)
end
# subject.rb
class Subject
def verify_user(user)
unless user.company.eql?(company)
raise "Error: current_user does not belongs to subject's company"
end
What happens if you delete the @ in front of @current_user in
def user_has_to_belongs_to_subject_company
unless @current_user.company.eql?(@subject.company)
to get
def user_has_to_belongs_to_subject_company
unless current_user.company.eql?(@subject.company)
And in your specs, do controller.stub!(:current_user).and_return @current_user
I think the problem is one of scope - @current_user in your tests is different to @current_user in your controller. Really depends on how "sign_in @current_user" is implemented.
Also, instead of raising an exception, perhaps your before_filter could redirect the user to another page and set flash[:error]? The before filter is the right place to handle this situation, so it shouldn't raise an exception that would have to be rescued somewhere else (or if not, it would display a 500 page to the user).
精彩评论