[rspec-users] Mocking Access Control

Courtenay court3nay at gmail.com
Thu Jul 26 17:22:36 EDT 2007


For starters, refactor your user<-->roles interaction.

class User

  def has_role?(name)
    role = Role.find_by_name(name)
    roles.include?(role)
  end

end

Trust me, this will make things much easier to spec, and later, to
scale.  Also, it keeps the DB-specific stuff ("find") in the model,
where it belongs.

It is my belief that if you see a "find" call in the controller, you
could probably refactor it and make it easier to maintain, and just
plain better.

For example, this will only make one DB call per role check for a
total of ~5 db calls

  def has_role?(name)
    roles.count(:conditions => { :name => name }) > 0
  end

This will make one db call to retrieve the list of role names for a
total of 1 db call for the whole action.

  def has_role?(name)
    @role_names ||= roles.map(&:name)
    @role_names.include?(name)
  end

So you can change the implementation without screwing round with a
bunch of tests. In fact neither of these would require a change of
controller specs.

  @user.should_receive(:has_role?).with('tutor').and_return(true)

Hope this helps :)

Courtenay


On 7/26/07, Justin Williams <carpeaqua at gmail.com> wrote:
> I've done some more work on the specs, and it seems that my mocks
> aren't pushing in the roles array associated with current_user.
>
>
> describe UsersController do
>   before(:each) do
>     @user = mock_model(User,
>       :id => 1,
>       :email => 'teamup at teamup.host',
>       :password => 'teamup'
>     )
>
>     controller.stub!(:current_user).and_return(@user)
>   end
>
>   it "should login as a tutor" do
>     @role = mock_model(Role)
>     @role.stub!(:title).and_return("tutor")
>     @user.should_receive(:roles).and_return([@role])
>     @user.stub!(:type).and_return("Tutor")
>
>     User.should_receive(:authenticate).with('teamup at teamup.host','teamup').and_return(@user)
>     session[:user] = @user.id
>     post :login, :login => {:email => "teamup at teamup.host", :password
> => "teamup"}
>     response.should be_success
>     response.should redirect_to(:controller => "toolkit/overview")
>     should_be_logged_in
>   end
> end
>
>
> The error i receive is  'UsersController should login as a tutor'
> FAILED expected redirect to {:controller=>"toolkit/overview"}, got no
> redirect".  If I modify the test to be should render_template("index")
> it will fail, saying that the template is reverting back to login.
>
> Any ideas on what I'm doing wrong?
>
> Thanks!
>
> - j
>
> On 7/24/07, Justin Williams <carpeaqua at gmail.com> wrote:
> > On 7/24/07, David Chelimsky <dchelimsky at gmail.com> wrote:
> >
> > > Would you please post the code for the actions as well?
> >
> > def login
> >   if request.post?
> >   begin
> >     session[:user] = User.authenticate(params[:login][:email],
> > params[:login][:password]).id
> >
> >     if current_user.roles.include?(Role.find_by_title("employee")) or
> > current_user.roles.include?(Role.find_by_title("administrator"))
> >       redirect_to staff_path
> >     elsif current_user.roles.include?(Role.find_by_title("tutor"))
> >       redirect_to toolkit_path
> >     elsif current_user.roles.include?(Role.find_by_title("client"))
> >       redirect_to client_path
> >     end
> >    rescue
> >      flash[:warning] = "Your e-mail address or password is invalid."
> >      render :action => "login"
> >    end
> >   end
> > end
> >
> >
> > It should also be noted, I realized the specs have two different sets
> > of credentials.  Modifying this to use a single one doesn't correct
> > it.  I was just a bit too liberal in my cut/pasting for email.
> >
> > Thanks!
> >
> > - j
> >
>
>
> --
> -
> Justin Williams
> justin at carpeaqua.com
> work: http://www.secondgearllc.com/
> play: http://www.carpeaqua.com
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list