Evgeny Bogdanov
2009-Feb-20 16:17 UTC
[rspec-users] How to mock an object defined in the before_filter function?
Hello, I am trying to implement the following scenario, but I am stuck ... The thing is I want to initialize a variable @comment when the stub function "find_comment" is called. The way I do it below doesn''t work, since "before_filter :find_comment" returns true/false and @comment initialization is done inside it. Could you please give me a hint how to do it? One solution would be to use Comment.stub!(:find).and_return(@comment) and do not use the stub find_comment. But how to do it in general, when there is no expression like "@var my_var.function" in the controller and variable @var is defined in another place and controller just uses it. my_var.stub!(:function).and_return(@var) doesn''t seem to be working. Is there is something like my_var.stub!(:function).add_variable(@var).and_return(:true) ? Thank you, Evgeny =============controller_file before_filter :find_comment, :only => [:destroy] def destroy @destroy_id = @comment.id #to be used in rendering partial @comment.destroy respond_to do |format| format.js end end def find_comment @comment = Comment.find(:first, :conditions => [''id= ?'',params [:comment_id]]) end ===============spec_file spec code describe CommentsController, "while deleting a comment" do it "should render destroy.rjs in case of success" do @comment = mock_model(Comment) @comment.stub!(:id).and_return(1) @comment.stub!(:destroy).and_return(:true) controller.stub!(:find_comment).and_return(@comment) # execute ajax request request.env["HTTP_ACCEPT"] = "application/javascript" post :destroy, :comment_id => 1, :item_id => 1, :item_type => "Space" response.should render_template("destroy") end end
David Chelimsky
2009-Feb-23 19:32 UTC
[rspec-users] How to mock an object defined in the before_filter function?
On Fri, Feb 20, 2009 at 10:17 AM, Evgeny Bogdanov <evgeny.bogdanov at gmail.com> wrote:> Hello, > > I am trying to implement the following scenario, but I am stuck ... > The thing is I want to initialize a variable @comment when the stub > function "find_comment" is called. > The way I do it below doesn''t work, since > "before_filter :find_comment" returns true/false and @comment > initialization is done inside it. Could you please give me a hint how > to do it? > > One solution would be to use > Comment.stub!(:find).and_return(@comment) and do not use the stub > find_comment.That''s the way to do it.> But how to do it in general, when there is no expression like "@var > my_var.function" in the controller and variable @var is defined in > another place and controller just uses it.In general, it''s best to avoid dealing directly with internal state on an object that you''re specifying and only manipulate its state through public methods and/or mocks/stubs on collaborators. So in this case, I''d just stub Comment.find, as mentioned above.> my_var.stub!(:function).and_return(@var) doesn''t seem to be working. > > Is there is something like > my_var.stub!(:function).add_variable(@var).and_return(:true)Nope. For the reasons stated above. This is too invasive. Cheers, David> ? > > Thank you, > Evgeny > =============controller_file > before_filter :find_comment, :only => [:destroy] > > def destroy > @destroy_id = @comment.id #to be used in rendering partial > @comment.destroy > > respond_to do |format| > format.js > end > end > > def find_comment > @comment = Comment.find(:first, :conditions => [''id= ?'',params > [:comment_id]]) > end > ===============spec_file > spec code > describe CommentsController, "while deleting a comment" do > it "should render destroy.rjs in case of success" do > @comment = mock_model(Comment) > @comment.stub!(:id).and_return(1) > @comment.stub!(:destroy).and_return(:true) > > controller.stub!(:find_comment).and_return(@comment) > > # execute ajax request > request.env["HTTP_ACCEPT"] = "application/javascript" > post :destroy, :comment_id => 1, :item_id => 1, :item_type => > "Space" > > response.should render_template("destroy") > end > end > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Evgeny Bogdanov
2009-Feb-25 09:57 UTC
[rspec-users] How to mock an object defined in the before_filter function?
Thank you, David! One small follow-up question. Is there is a way to mock an object "@comment" in the controller_spec for the function? I didn''t manage to do it. def destroy @destroy_id = @comment.id #to be used in rendering partial @comment.destroy end Looks like assigns[:comment] = mock("comment") works only for views? Thank you, Evgeny On Feb 23, 8:32?pm, David Chelimsky <dchelim... at gmail.com> wrote:> On Fri, Feb 20, 2009 at 10:17 AM, Evgeny Bogdanov > > <evgeny.bogda... at gmail.com> wrote: > > Hello, > > > I am trying to implement the following scenario, but I am stuck ... > > The thing is I want to initialize a variable @comment when the stub > > function "find_comment" is called. > > The way I do it below doesn''t work, since > > "before_filter :find_comment" returns true/false and @comment > > initialization is done inside it. Could you please give me a hint how > > to do it? > > > One solution would be to use > > Comment.stub!(:find).and_return(@comment) and do not use the stub > > find_comment. > > That''s the way to do it. > > > But how to do it in general, when there is no expression like "@var > > my_var.function" in the controller and variable @var is defined in > > another place and controller just uses it. > > In general, it''s best to avoid dealing directly with internal state on > an object that you''re specifying and only manipulate its state through > public methods and/or mocks/stubs on collaborators. So in this case, > I''d just stub Comment.find, as mentioned above. > > > my_var.stub!(:function).and_return(@var) doesn''t seem to be working. > > > Is there is something like > > my_var.stub!(:function).add_variable(@var).and_return(:true) > > Nope. For the reasons stated above. This is too invasive. > > Cheers, > David > > > > > ? > > > Thank you, > > Evgeny > > =============controller_file > > ?before_filter :find_comment, :only => [:destroy] > > > ?def destroy > > ? ?@destroy_id = @comment.id #to be used in rendering partial > > ? ?@comment.destroy > > > ? ?respond_to do |format| > > ? ? ?format.js > > ? ?end > > ?end > > > ?def find_comment > > ? ? @comment = Comment.find(:first, :conditions => [''id= ?'',params > > [:comment_id]]) > > ?end > > ===============spec_file > > spec code > > describe CommentsController, "while deleting a comment" do > > ?it "should render destroy.rjs in case of success" do > > ? ?@comment = mock_model(Comment) > > ? ?@comment.stub!(:id).and_return(1) > > ? ?@comment.stub!(:destroy).and_return(:true) > > > ? ?controller.stub!(:find_comment).and_return(@comment) > > > ? ?# execute ajax request > > ? ?request.env["HTTP_ACCEPT"] = "application/javascript" > > ? ?post :destroy, :comment_id => 1, :item_id => 1, :item_type => > > "Space" > > > ? ?response.should render_template("destroy") > > ?end > > end > > _______________________________________________ > > rspec-users mailing list > > rspec-us... at rubyforge.org > >http://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
Pat Maddox
2009-Feb-25 10:42 UTC
[rspec-users] How to mock an object defined in the before_filter function?
Can you show your spec? I''m not sure why you''re using assigns in the controller spec. David explained that it doesn''t work like that. Your spec should look something like it "should destroy the record" do mock_comment = stub_model Comment Comment.stub!(:find).and_return mock_comment mock_comment.should_receive(:destroy) delete :destroy, :id => mock_comment.to_param end Pat On Wed, Feb 25, 2009 at 1:57 AM, Evgeny Bogdanov <evgeny.bogdanov at gmail.com> wrote:> Thank you, David! > One small follow-up question. > Is there is a way to mock an object "@comment" in the controller_spec > for the function? > I didn''t manage to do it. > > def destroy > ? ?@destroy_id = @comment.id #to be used in rendering partial > ? ?@comment.destroy > end > > Looks like assigns[:comment] = mock("comment") works only for views? > > Thank you, > Evgeny > > > On Feb 23, 8:32?pm, David Chelimsky <dchelim... at gmail.com> wrote: >> On Fri, Feb 20, 2009 at 10:17 AM, Evgeny Bogdanov >> >> <evgeny.bogda... at gmail.com> wrote: >> > Hello, >> >> > I am trying to implement the following scenario, but I am stuck ... >> > The thing is I want to initialize a variable @comment when the stub >> > function "find_comment" is called. >> > The way I do it below doesn''t work, since >> > "before_filter :find_comment" returns true/false and @comment >> > initialization is done inside it. Could you please give me a hint how >> > to do it? >> >> > One solution would be to use >> > Comment.stub!(:find).and_return(@comment) and do not use the stub >> > find_comment. >> >> That''s the way to do it. >> >> > But how to do it in general, when there is no expression like "@var >> > my_var.function" in the controller and variable @var is defined in >> > another place and controller just uses it. >> >> In general, it''s best to avoid dealing directly with internal state on >> an object that you''re specifying and only manipulate its state through >> public methods and/or mocks/stubs on collaborators. So in this case, >> I''d just stub Comment.find, as mentioned above. >> >> > my_var.stub!(:function).and_return(@var) doesn''t seem to be working. >> >> > Is there is something like >> > my_var.stub!(:function).add_variable(@var).and_return(:true) >> >> Nope. For the reasons stated above. This is too invasive. >> >> Cheers, >> David >> >> >> >> > ? >> >> > Thank you, >> > Evgeny >> > =============controller_file >> > ?before_filter :find_comment, :only => [:destroy] >> >> > ?def destroy >> > ? ?@destroy_id = @comment.id #to be used in rendering partial >> > ? ?@comment.destroy >> >> > ? ?respond_to do |format| >> > ? ? ?format.js >> > ? ?end >> > ?end >> >> > ?def find_comment >> > ? ? @comment = Comment.find(:first, :conditions => [''id= ?'',params >> > [:comment_id]]) >> > ?end >> > ===============spec_file >> > spec code >> > describe CommentsController, "while deleting a comment" do >> > ?it "should render destroy.rjs in case of success" do >> > ? ?@comment = mock_model(Comment) >> > ? ?@comment.stub!(:id).and_return(1) >> > ? ?@comment.stub!(:destroy).and_return(:true) >> >> > ? ?controller.stub!(:find_comment).and_return(@comment) >> >> > ? ?# execute ajax request >> > ? ?request.env["HTTP_ACCEPT"] = "application/javascript" >> > ? ?post :destroy, :comment_id => 1, :item_id => 1, :item_type => >> > "Space" >> >> > ? ?response.should render_template("destroy") >> > ?end >> > end >> > _______________________________________________ >> > rspec-users mailing list >> > rspec-us... at rubyforge.org >> >http://rubyforge.org/mailman/listinfo/rspec-users >> >> _______________________________________________ >> rspec-users mailing list >> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Evgeny Bogdanov
2009-Feb-25 22:40 UTC
[rspec-users] How to mock an object defined in the before_filter function?
The function to specify and specification for it are below. Comment.stub!(:find).and_return(@comment) does what I need. However, it strongly depends on the implementation of find_comment function, and if I change it to, let''s say, this one: def find_comment @space = Space.find(1) @comment = @space.comment end my spec will fail. Moreover, in my spec I''m checking if the destroy.rjs is returned on request, and I don''t care about @comment and find_comment. Thus, I would like just to mock the whole object @comment for the function "destroy" like it is done with assigns for view specs, so that my spec doesn''t depend on it at all. Best, Evgeny PS. Sorry for bother @comment = Space.comment, the test will fail. before_filter :find_comment def find_comment @comment = Comment.find(:first, :conditions => [''id= ?'',params [:comment_id]]) end def destroy @destroy_id = @comment.id #to be used in rendering partial @comment.destroy respond_to do |format| format.js end end describe CommentsController, "while deleting a comment" do it "should render destroy.rjs in case of success" do # stub find_comment call @comment = mock_model(Comment) @comment.stub!(:id).and_return(1) @comment.stub!(:destroy).and_return(:true) Comment.stub!(:find).and_return(@comment) request.env["HTTP_ACCEPT"] = "application/javascript" post :destroy, :comment_id => 1, :item_id => 1, :item_type => "Space" response.should render_template("destroy") end end On Feb 25, 11:42?am, Pat Maddox <pat.mad... at gmail.com> wrote:> Can you show your spec? ?I''m not sure why you''re using assigns in the > controller spec. ?David explained that it doesn''t work like that. > Your spec should look something like > > it "should destroy the record" do > ? mock_comment = stub_model Comment > ? Comment.stub!(:find).and_return mock_comment > ? mock_comment.should_receive(:destroy) > ? delete :destroy, :id => mock_comment.to_param > end > > Pat > > On Wed, Feb 25, 2009 at 1:57 AM, Evgeny Bogdanov > > <evgeny.bogda... at gmail.com> wrote: > > Thank you, David! > > One small follow-up question. > > Is there is a way to mock an object "@comment" in the controller_spec > > for the function? > > I didn''t manage to do it. > > > def destroy > > ? ?@destroy_id = @comment.id #to be used in rendering partial > > ? ?@comment.destroy > > end > > > Looks like assigns[:comment] = mock("comment") works only for views? > > > Thank you, > > Evgeny > > > On Feb 23, 8:32?pm, David Chelimsky <dchelim... at gmail.com> wrote: > >> On Fri, Feb 20, 2009 at 10:17 AM, Evgeny Bogdanov > > >> <evgeny.bogda... at gmail.com> wrote: > >> > Hello, > > >> > I am trying to implement the following scenario, but I am stuck ... > >> > The thing is I want to initialize a variable @comment when the stub > >> > function "find_comment" is called. > >> > The way I do it below doesn''t work, since > >> > "before_filter :find_comment" returns true/false and @comment > >> > initialization is done inside it. Could you please give me a hint how > >> > to do it? > > >> > One solution would be to use > >> > Comment.stub!(:find).and_return(@comment) and do not use the stub > >> > find_comment. > > >> That''s the way to do it. > > >> > But how to do it in general, when there is no expression like "@var > >> > my_var.function" in the controller and variable @var is defined in > >> > another place and controller just uses it. > > >> In general, it''s best to avoid dealing directly with internal state on > >> an object that you''re specifying and only manipulate its state through > >> public methods and/or mocks/stubs on collaborators. So in this case, > >> I''d just stub Comment.find, as mentioned above. > > >> > my_var.stub!(:function).and_return(@var) doesn''t seem to be working. > > >> > Is there is something like > >> > my_var.stub!(:function).add_variable(@var).and_return(:true) > > >> Nope. For the reasons stated above. This is too invasive. > > >> Cheers, > >> David > > >> > ? > > >> > Thank you, > >> > Evgeny > >> > =============controller_file > >> > ?before_filter :find_comment, :only => [:destroy] > > >> > ?def destroy > >> > ? ?@destroy_id = @comment.id #to be used in rendering partial > >> > ? ?@comment.destroy > > >> > ? ?respond_to do |format| > >> > ? ? ?format.js > >> > ? ?end > >> > ?end > > >> > ?def find_comment > >> > ? ? @comment = Comment.find(:first, :conditions => [''id= ?'',params > >> > [:comment_id]]) > >> > ?end > >> > ===============spec_file > >> > spec code > >> > describe CommentsController, "while deleting a comment" do > >> > ?it "should render destroy.rjs in case of success" do > >> > ? ?@comment = mock_model(Comment) > >> > ? ?@comment.stub!(:id).and_return(1) > >> > ? ?@comment.stub!(:destroy).and_return(:true) > > >> > ? ?controller.stub!(:find_comment).and_return(@comment) > > >> > ? ?# execute ajax request > >> > ? ?request.env["HTTP_ACCEPT"] = "application/javascript" > >> > ? ?post :destroy, :comment_id => 1, :item_id => 1, :item_type => > >> > "Space" > > >> > ? ?response.should render_template("destroy") > >> > ?end > >> > end > >> > _______________________________________________ > >> > rspec-users mailing list > >> > rspec-us... at rubyforge.org > >> >http://rubyforge.org/mailman/listinfo/rspec-users > > >> _______________________________________________ > >> rspec-users mailing list > >> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > > rspec-users mailing list > > rspec-us... at rubyforge.org > >http://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
Pat Maddox
2009-Feb-26 01:03 UTC
[rspec-users] How to mock an object defined in the before_filter function?
On Wed, Feb 25, 2009 at 2:40 PM, Evgeny Bogdanov <evgeny.bogdanov at gmail.com> wrote:> The function to specify and specification for it are below. > > Comment.stub!(:find).and_return(@comment) > does what I need. > However, it strongly depends on the implementation of find_comment > function, > and if I change it to, let''s say, this one: > ? ?def find_comment > ? ? ? @space = Space.find(1) > ? ? ? @comment = @space.comment > ? ? endMy personal opinion is that the semantics of your app have changed, so at least some spec somewhere should have to change with it. There are a lot of people who don''t agree with that.> my spec will fail. Moreover, in my spec I''m checking if the > destroy.rjs is returned on request, > and I don''t care about @comment and find_comment. Thus, I would like > just to mock the whole > object @comment for the function "destroy" like it is done with > assigns for view specs, so that my spec doesn''t depend on it at all.I would wrap the stubbing stuff in a method, so that you clearly show in your test that you don''t care about the comment. e.g. # spec_helper.rb def stub_comment c = mock_model(Comment) Comment.stub!(:find).and_return c c end describe CommentsController, "DELETE /comments/1" do before(:each) do stub_comment end ... end Another alternative is to not use a @comment ivar in your controllers and instead to use a method. e.g. def comment @comment ||= Comment.find params[:id] end and wherever you reference @comment in the controller, ref the method comment instead. Then in your spec you can stub it. You need to inject your mock somehow, but due to the design of Rails controllers you can''t really just "set" something in there, so you need to either use a factory method which you stub in the controller spec, or stub out the finder. If you think that the setup is too verbose, wrap it in an intention-revealing method. Pat Pat
Evgeny Bogdanov
2009-Feb-26 08:34 UTC
[rspec-users] How to mock an object defined in the before_filter function?
Thanks a lot Pat for clear and detailed explanations. My initial vision was that I don''t change code and do all the manipulations in spec files. Now I understand that it is not really what I should be doing. I should take into account both, and adapt one to another to improve clarity and briefness of both ones. This might bring me to a better programming style :) On Feb 26, 2:03?am, Pat Maddox <pat.mad... at gmail.com> wrote:> On Wed, Feb 25, 2009 at 2:40 PM,EvgenyBogdanov > > <evgeny.bogda... at gmail.com> wrote: > > The function to specify and specification for it are below. > > > Comment.stub!(:find).and_return(@comment) > > does what I need. > > However, it strongly depends on the implementation of find_comment > > function, > > and if I change it to, let''s say, this one: > > ? ?def find_comment > > ? ? ? @space = Space.find(1) > > ? ? ? @comment = @space.comment > > ? ? end > > My personal opinion is that the semantics of your app have changed, so > at least some spec somewhere should have to change with it. ?There are > a lot of people who don''t agree with that. > > > my spec will fail. Moreover, in my spec I''m checking if the > > destroy.rjs is returned on request, > > and I don''t care about @comment and find_comment. Thus, I would like > > just to mock the whole > > object @comment for the function "destroy" like it is done with > > assigns for view specs, so that my spec doesn''t depend on it at all. > > I would wrap the stubbing stuff in a method, so that you clearly show > in your test that you don''t care about the comment. ?e.g. > > # spec_helper.rb > def stub_comment > ? c = mock_model(Comment) > ? Comment.stub!(:find).and_return c > ? c > end > > describe CommentsController, "DELETE /comments/1" do > ? before(:each) do > ? ? stub_comment > ? end > > ? ... > end > > Another alternative is to not use a @comment ivar in your controllers > and instead to use a method. ?e.g. > > def comment > ? @comment ||= Comment.find params[:id] > end > > and wherever you reference @comment in the controller, ref the method > comment instead. ?Then in your spec you can stub it. > > You need to inject your mock somehow, but due to the design of Rails > controllers you can''t really just "set" something in there, so you > need to either use a factory method which you stub in the controller > spec, or stub out the finder. ?If you think that the setup is too > verbose, wrap it in an intention-revealing method. > > Pat > > Pat > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users