Tiffani Ashley Bell
2008-Jul-03 22:32 UTC
[rspec-users] 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 -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20080703/051cf0d4/attachment-0001.html>
On Fri, Jul 4, 2008 at 8:32 AM, Tiffani Ashley Bell <tiffani2k3 at gmail.com> 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! -- http://lindsaar.net/ Rails, RSpec, Puppet and Life blog....
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 -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20080703/54b86366/attachment.html>
On Jul 3, 2008, at 10:55 PM, Mark Wilden 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?> > > ///ark > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
Mark Wilden
2008-Jul-04 16:17 UTC
[rspec-users] Fwd: Attempting to Make Sense of RSpec use
On Fri, Jul 4, 2008 at 6:28 AM, David Chelimsky <dchelimsky at gmail.com> wrote:> On Jul 3, 2008, at 10:55 PM, Mark Wilden 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? >I didn''t think of that - I''ll give it a shot next time I''m in those specs. ///ark -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20080704/34d0cb3e/attachment-0001.html>
Tiffani Ashley Bell
2008-Jul-05 19:50 UTC
[rspec-users] Attempting to Make Sense of RSpec use
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! :D --Tiffani AB On Thu, Jul 3, 2008 at 9:09 PM, Mikel Lindsaar <raasdnil at gmail.com> wrote:> On Fri, Jul 4, 2008 at 8:32 AM, Tiffani Ashley Bell > <tiffani2k3 at gmail.com> 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! > > -- > http://lindsaar.net/ > Rails, RSpec, Puppet and Life blog.... > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20080705/ec664a0e/attachment-0001.html>
On Sat, Jul 5, 2008 at 3:50 PM, Tiffani Ashley Bell <tiffani2k3 at gmail.com> 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->> > Thanks in advance for answering my RSpec questions! :D > > --Tiffani AB > > > On Thu, Jul 3, 2008 at 9:09 PM, Mikel Lindsaar <raasdnil at gmail.com> wrote: >> >> On Fri, Jul 4, 2008 at 8:32 AM, Tiffani Ashley Bell >> <tiffani2k3 at gmail.com> 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! >> >> -- >> http://lindsaar.net/ >> Rails, RSpec, Puppet and Life blog.... >> _______________________________________________ >> rspec-users mailing list >> rspec-users at rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- Have Fun, Steve Eley Deep Salt Team
Tiffani Ashley Bell
2008-Jul-05 21:56 UTC
[rspec-users] Attempting to Make Sense of RSpec use
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 5:00 PM, Steve Eley <steve at deepsalt.com> wrote:> On Sat, Jul 5, 2008 at 3:50 PM, Tiffani Ashley Bell > <tiffani2k3 at gmail.com> 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-> > > > > > > > > Thanks in advance for answering my RSpec questions! :D > > > > --Tiffani AB > > > > > > On Thu, Jul 3, 2008 at 9:09 PM, Mikel Lindsaar <raasdnil at gmail.com> > wrote: > >> > >> On Fri, Jul 4, 2008 at 8:32 AM, Tiffani Ashley Bell > >> <tiffani2k3 at gmail.com> 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! > >> > >> -- > >> http://lindsaar.net/ > >> Rails, RSpec, Puppet and Life blog.... > >> _______________________________________________ > >> rspec-users mailing list > >> rspec-users at rubyforge.org > >> http://rubyforge.org/mailman/listinfo/rspec-users > > > > > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > > > -- > Have Fun, > Steve Eley > Deep Salt Team > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20080705/00db956f/attachment-0001.html>