[rspec-users] [RSpec] [Rails] [BDD]

Ben Mabey ben at benmabey.com
Sat Feb 28 15:21:05 EST 2009

julian mann wrote:
> Hello,
> My first time here, and I'm pretty new to rspec, and indeed BDD/TDD, 
> so bear with me

Welcome Julian!  See my comments inline below...
> I am trying to spec a fairly simple controller update action for an 
> account. The account belongs_to :user and user has_one :account.
>  the route is PUT: /users/1/account
> here's the action
>  def update
>     @account = @user.account
>     respond_to do |format|
>       if @account.update_attributes(params[:account])
>         flash[:notice] = ' Account was successfully updated.'
>         format.html { redirect_to(user_account_path( @user)) }
>       else
>         format.html { render :action => "edit" }
>       end
>     end
>   end
> I'm trying to avoid fixtures, and I'm using rspec's own mock framework
> In normal operation the @user instance variable is assigned in a 
> before_filter (get_user) - so
> i figured if I stub that I could then assign user:
> before(:each) do
> controller.stub!(:get_user).and_return(true)
> instance_variable_set('@user', mock_model(User))
> end
> I tried other variations on this - like instance_variable_set('@user', 
> User.new)  and others
> but @user is never assigned 

When you use instance_variable_set you are setting the variable inside 
the example group (the test) and not the actual controller it self. 
In order to use your mock user in your controller you need to stub 
whatever your get_user method uses to find the user.  You do not want to 
stub out the get_user method otherwise your controller will never be 
able to set the user object. For example, say you have this:

def get_user
  @user = User.find(session[:user])

In order to stub this correctly you would not stub get_user but instead 
stub the find call on User, like so:

before(:each) do
  User.stub!(:find).and_return( @user = mock_model(User))

Doing this will stub the user in the controller and also allow you to 
access the same object (as @user) in your example group. ( The fact that 
I named it @user in the example has nothing to do with it- that is just 
to be consistent.)

On another note, what many rails apps do is instead of using filters to 
set instance variables you can extract it into an accessor method that 
caches it.  For example, instead of having the get_user method as a 
filter you could simply have this:

def current_user
  @current_user ||= User.find(session[:user])

Then in your action:

def update
    @account = current_user.account

To stub this you could do what I listed above or stub out the 
current_user method like so:

before(:each) do
  controller.stub!(:current_user).and_return( @current_user = 

In general you should not stub or mock methods on the object you are 
trying to test.  That said, stubbing current_user on the controller is 
something that is done often and is usually a low-risk thing to do.  (I 
just thought I would point out the bad smell.)

Now, to stub out the account on user, in your examples you would just 
need to say something like:


Or create the account stub inline with the user mock creation:

mock_model(User, :account => mock_model(Account))



More information about the rspec-users mailing list