Attempting to Make Sense of RSpec use

Hi everybody,

I’m pretty new to RSpec and have been reading over examples and
tutorials of
how to use the framework. I like everything so far since it goes right
along with Rails’ expressiveness and such.

I was reading the Typo source code, however, and came across some code
that
I didn’t know exactly how it worked. I’ve noticed that in testing one
of
their controllers, they use a variable (@comments) that they don’t
declare
anywhere else, yet they use it as a stand in for collections on some of
the
mocks. How is that possible? I know in the mocking documentation it
says
that you can define collaborations with other objects before those
objects
exist, but how is that working in this code? I only ask that because
later,
you see code like this: @comments.stub!(:build).and_return(@comment).

I’m looking at
http://svn.typosphere.org/typo/trunk/spec/controllers/comments_controller_spec.rband
http://svn.typosphere.org/typo/trunk/app/controllers/comments_controller.rb.

Thanks in advance for any ideas!
Tiffani AB

On Fri, Jul 4, 2008 at 8:32 AM, Tiffani Ashley B.
[email protected] wrote:

Hi everybody,

Hi Tiffany, welcome to Rspec

I was reading the Typo source code, however, and came across some code that
I didn’t know exactly how it worked. I’ve noticed that in testing one of
their controllers, they use a variable (@comments) that they don’t declare
anywhere else, yet they use it as a stand in for collections on some of the
mocks. How is that possible? I know in the mocking documentation it says
that you can define collaborations with other objects before those objects
exist, but how is that working in this code? I only ask that because later,
you see code like this: @comments.stub!(:build).and_return(@comment).

If you have a look at the descriptions, they use :shared => true.
This is a way of being DRY in RSpec (which I personally don’t think is
such a good idea).

What the shared => true declaration allows you to do is to include
that block of code elsewhere with ‘it should behave like my shared
code’

So we have (describe “All Requests”, :shared => true do)

and then the next description block is:

describe “General Comment Creation”, :shared => true do
it_should_behave_like “All Requests”

Which then includes the All Requests block (which is just a before
method).

The @comments variable gets declared in:

@comments.stub!(:build).and_return(@comment)

and then this is tied in to the Article model in the previous code
block like so:

@article  = mock_model(Article,
              :comments                   => @comments,
              :published_comments         => @comments,
              :add_comment                => @comment)

So when you call @article.comments you get @comments as a stub back
which stubs :build and returns a @comment.

Ugh.

This is where, in RSpec, you can dig a very fast grave. Because
you’ll come back to this code in 6-12 months and be totally stuck
trying to figure out what is where.

I recently wrote a viewpoint on this that might help you:
http://www.lindsaar.net/2008/6/24/tip-24-being-clever-in-specs-is-for-dummies

Hope you do well with Rspec, feel free to ask more questions!

On Jul 3, 2008, at 10:55 PM, Mark W. wrote:

The problem I just found with shared specs is that if one fails, you
don’t see the sharer in the callstack, so you really don’t know what
went wrong.

Even if you run it with --backtrace?

The problem I just found with shared specs is that if one fails, you
don’t
see the sharer in the callstack, so you really don’t know what went
wrong.

///ark

Awesome. I totally get it, but is that how you’re always supposed to
spec
out associations and all the methods that go with an association like
“create” and such? I’m interested in that because I’m specing a lot of
code
that deals heavily with code that has associations going on, yet none of
the
examples of RSpec use I come across have anything about associations and
RSpec. I don’t imagine they’d be handled too differently, but it’s just
something wondered about.

Right now, I’m testing and building up a controller and so far my tests
have
been passing as long as I don’t run into any examples that involve
associations. This code, for example, has been giving me a few issues:

my users_controller_spec.rb (or at least, the relevant part)

describe UsersController do
fixtures :users

before(:each) do
@tiffani = users(:tiffani)
@users = [@tiffani]
@users.stub!(:build).and_return(@tiffani)
@mock_account = mock_model(Account, :users => @users, :save => true)
end

describe “when creating a User” do
it “should return the registration form for adding a User on GET
new” do
User.should_receive(:new).once
get :new
end

it "should render the new User registration form on GET new" do
  get :new
  response.should render_template("users/new")
end

it "should create a new User and then redirect to that User's 

profile on
POST create" do
@mock_account.should_receive(:new)
post :create, :new_user => { :first_name => @tiffani.first_name,
:last_name => @tiffani.last_name,
:email_address =>
@tiffani.email_address }
end

it "should redirect to users/new when User is setup with invalid 

data"
end

end

And in users_controller.rb:

def create
@new_account = Account.new(params[:account])
@new_user = @new_account.users.build(params[:new_user])

respond_to do |wants|
  if @new_account.save
    flash[:notice] = "Welcome, #{ @new_user.first_name }!"
    wants.html { redirect_to(user_url(@new_user)) }
  else
    wants.html { render :action => "new" }
  end
end

end

When I run the tests the third test fails and RSpec complains that “Mock
‘Account_1003’ expected :new with (any args) once, but received it 0
times”

I’m confused about that since I am calling Account.new in the create
method
on the controller. What’s really wrong here?

Thanks in advance for answering my RSpec questions! :smiley:

–Tiffani AB

Thanks on the reminder that “new” is a class method. Plus, I figured
using
mocks and fixtures together was probably a crappy idea. I’ll be mocking
from now on…

–Tiffani AB

On Sat, Jul 5, 2008 at 3:50 PM, Tiffani Ashley B.
[email protected] wrote:

When I run the tests the third test fails and RSpec complains that “Mock
‘Account_1003’ expected :new with (any args) once, but received it 0 times”

I’m confused about that since I am calling Account.new in the create method
on the controller. What’s really wrong here?

The problem there is that Account.new is a class method on the
Account class. The @mock_account you made is an instance of Account
(actually, not even that, it’s a mock object that will pretend it’s an
Account if you ask it). You’re not sending @mock_account any
messages, you’re sending them to the Account class. To do what you
want, you need to stub that class, for instance:

Account.stub!(:new).and_return(@mock_account)

And in the spec you can do Account.should_receive(:new).

There’s some other stuff in that spec that looks a bit messy…
Generally speaking, you can do some pretty clean tests with fixtures
or you can do tests by mocking everything, but it’s not a great idea
to do both at the same time. In controller specs, best practice is
usually to mock your models and not touch the real models or the
database (i.e. fixtures) at all, because A.) it’s faster and B.)
you’re isolating your tests to just the controller code, and won’t
have to worry about tests failing because the models are broken.
(That’s what the model specs are for.) >8->

I’m also unclear on the relationship between User and Account in this
code, and why you’re creating a new account for every new user in the
UsersController… But that’s really about your application, not
about RSpec. If that’s how your application needs to behave, then
your spec here seems to be on the right track.

I hope this was helpful. I’m just figuring a lot of this out myself,
and my main reason for answering you was to reinforce this stuff in my
own mind. >8->

Hi everybody,
the
such a good idea).
it_should_behave_like “All Requests”


[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users


Have Fun,
Steve E.
Deep Salt Team