Following is the scenario. describe BbPostsController, "POST Create" do context "Admin" do fixtures :users, :bb_posts, :user_channels, :channels, :channel_features it "should save post" do login_as(:amit) # Added to pass before filters in controller controller.stub!(:validate_channel).and_return(true) controller.stub!(:is_feature_active).and_return(Blog) controller.stub!(:load_categories).and_return(1) @post = mock_model( BbPost, :body => "test_description", :title => "test123", :abstract => "test_abstract", :channel_feature_id => "v1", :published => "1", :bb_post_category_id => "1" ) BbPost.stub!(:new).and_return @post @post.should_receive( :save ) post :create, {:bb_post => {:title => ''test123''}} response.should redirect_to( blog_bb_posts_url(:channel => @channel.brand_name) ) end Following is the controller code: class BbPostsController < ApplicationController before_filter :login_required before_filter :validate_channel before_filter :manager_access_required before_filter :is_feature_active before_filter :load_categories, :only => [ :new, :edit, :create, :update ] # BEFORE FILTER : Feature must active def is_feature_active @feature = @channel.channel_features.by_name( BLOG ).active.first unless @feature flash[ :notice ] = ''Blog feature was not active.'' redirect_to requests_features_path end end # BEFORE FILTER : Load blog categories def load_categories # manager blog will not have admin specific categories @categories = ( params[ :channel ].camelize == ADMIN_CHANNEL ) ? BbPostCategory.visible : BbPostCategory.visible.without_admin end def create params[ :bb_post ][ :brand_list ] = ADMIN_CHANNEL if params[ :bb_post ][ :brand_list ].blank? p params @bb_post = @feature.posts.new( params[ :bb_post ] ) @bb_post.user = current_user # CREATE Channel Blog post respond_to do |format| if @bb_post.save flash[ :notice ] = ''Blog post was successfully created.'' format.html { redirect_to( blog_bb_posts_url ) } format.xml { render :xml => @bb_post, :status => :created, :location => @bb_post } else load_featured_posts if @bb_post.editorial? format.html { render :action => "new" } format.xml { render :xml => @bb_post.errors, :status => :unprocessable_entity } end end end Now if i run my specs,i get an error which says NoMethodError in ''BbPostsController POST Crete dmin should sve post'' You hve nil object when you didn''t expect it! The error occurred while evluting nil.posts Error comes at "@bb_post = @feature.posts.new( params[ :bb_post ] )" Just above that line i tried to print params and i get following values: #<BbPost:0x44d1cd8 @nme="BbPost_1001"> {"ction"=>"crete", "controller"=>"bb_posts", "bb_post"=>{"brnd_list"=>"BrndPotio n", "title"=>"test123"}} Can somebody help me with this Also by using controller.stub!(:validate_channel).and_return(true) can somebody tell me actually,what exactly happens here.How does it handle before filters. -- Posted via http://www.ruby-forum.com/.
On Nov 2, 2009, at 3:35 AM, Amit Kulkarni wrote:> Following is the scenario. > > describe BbPostsController, "POST Create" do > context "Admin" do > fixtures :users, :bb_posts, :user_channels, :channels, > :channel_features > it "should save post" do > login_as(:amit) > # Added to pass before filters in controller > controller.stub!(:validate_channel).and_return(true) > controller.stub!(:is_feature_active).and_return(Blog) > controller.stub!(:load_categories).and_return(1) > @post = mock_model( BbPost, :body => "test_description", :title > => "test123", :abstract => "test_abstract", :channel_feature_id => > "v1", > :published => "1", :bb_post_category_id => "1" ) > BbPost.stub!(:new).and_return @post > @post.should_receive( :save ) > post :create, {:bb_post => {:title => ''test123''}} > response.should redirect_to( blog_bb_posts_url(:channel => > @channel.brand_name) ) > end > > Following is the controller code: > class BbPostsController < ApplicationController > before_filter :login_required > before_filter :validate_channel > before_filter :manager_access_required > before_filter :is_feature_active > before_filter :load_categories, :only => [ :new, :edit, :create, > :update ] > > # BEFORE FILTER : Feature must active > def is_feature_active > @feature = @channel.channel_features.by_name( BLOG ).active.first > unless @feature > flash[ :notice ] = ''Blog feature was not active.'' > redirect_to requests_features_path > end > end > > # BEFORE FILTER : Load blog categories > def load_categories > # manager blog will not have admin specific categories > @categories = ( params[ :channel ].camelize == ADMIN_CHANNEL ) ? > BbPostCategory.visible : BbPostCategory.visible.without_admin > end > > def create > params[ :bb_post ][ :brand_list ] = ADMIN_CHANNEL if params[ > :bb_post ][ :brand_list ].blank? > p params > @bb_post = @feature.posts.new( params[ :bb_post ] ) > @bb_post.user = current_user > # CREATE Channel Blog post > respond_to do |format| > if @bb_post.save > flash[ :notice ] = ''Blog post was successfully created.'' > format.html { redirect_to( blog_bb_posts_url ) } > format.xml { render :xml => @bb_post, :status => :created, > :location => @bb_post } > else > load_featured_posts if @bb_post.editorial? > format.html { render :action => "new" } > format.xml { render :xml => @bb_post.errors, :status => > :unprocessable_entity } > end > end > end > > Now if i run my specs,i get an error which says > > NoMethodError in ''BbPostsController POST Crete dmin should sve post'' > You hve nil object when you didn''t expect it! > The error occurred while evluting nil.posts > Error comes at "@bb_post = @feature.posts.new( params[ :bb_post ] )"This tells you that @feature is nil. This is because filter that declares @feature and assigns it a value is being stubbed. Although very common, using filters to set instance variables makes it difficult to test in isolation. I recommend pushing the model concerns to the model, and use filters for application flow control concerns (like authentication/ authorization) in the controller. In this case, that would mean eliminating the validate_channel, is_feature_active, and load_categories filters and simply passing the params to the model. I''m not sure of the models and relationships, but I''m imagining something like: def create @bb_post = BbPost.new( params[ :bb_post ].merge(:user => current_user) ) respond_to do |format| if @bb_post.save ... All of the logic in the filters and before the assignment of @bb_post can be managed in the BbPost model, where it is far easier to spec in my experience. If you don''t want to follow that recommendation, you''ll have to either set up all the state you need in the database for each example, or do more invasive setup like this: controller.instance_eval { @feature = mock(''feature'') }> Just above that line i tried to print params and i get following > values: > #<BbPost:0x44d1cd8 @nme="BbPost_1001"> > {"ction"=>"crete", "controller"=>"bb_posts", > "bb_post"=>{"brnd_list"=>"BrndPotio > n", "title"=>"test123"}} > > Can somebody help me with this > Also by using controller.stub!(:validate_channel).and_return(true) can > somebody tell me actually,what exactly happens here.How does it handle > before filters.Filters are just methods that get called implicitly by the controller. RSpec''s mocking framework doesn''t handle them in any special way (nor does any test double frameowrk that I''m aware of). Stubs return values. They do not set state on the object in question, which is why if you stub is_feature_active, for example, that needs to be coupled with code that sets the value of @feature on the controller (per above). HTH, David
On 2 Nov 2009, at 14:19, David Chelimsky wrote:> On Nov 2, 2009, at 3:35 AM, Amit Kulkarni wrote: > >> Following is the scenario. >> >> describe BbPostsController, "POST Create" do >> context "Admin" do >> fixtures :users, :bb_posts, :user_channels, :channels, >> :channel_features >> it "should save post" do >> login_as(:amit) >> # Added to pass before filters in controller >> controller.stub!(:validate_channel).and_return(true) >> controller.stub!(:is_feature_active).and_return(Blog) >> controller.stub!(:load_categories).and_return(1) >> @post = mock_model( BbPost, :body => "test_description", :title >> => "test123", :abstract => "test_abstract", :channel_feature_id => >> "v1", >> :published => "1", :bb_post_category_id => "1" ) >> BbPost.stub!(:new).and_return @post >> @post.should_receive( :save ) >> post :create, {:bb_post => {:title => ''test123''}} >> response.should redirect_to( blog_bb_posts_url(:channel => >> @channel.brand_name) ) >> end >> >> Following is the controller code: >> class BbPostsController < ApplicationController >> before_filter :login_required >> before_filter :validate_channel >> before_filter :manager_access_required >> before_filter :is_feature_active >> before_filter :load_categories, :only => [ :new, :edit, :create, >> :update ] >> >> # BEFORE FILTER : Feature must active >> def is_feature_active >> @feature = @channel.channel_features.by_name( BLOG ).active.first >> unless @feature >> flash[ :notice ] = ''Blog feature was not active.'' >> redirect_to requests_features_path >> end >> end >> >> # BEFORE FILTER : Load blog categories >> def load_categories >> # manager blog will not have admin specific categories >> @categories = ( params[ :channel ].camelize == ADMIN_CHANNEL ) ? >> BbPostCategory.visible : BbPostCategory.visible.without_admin >> end >> >> def create >> params[ :bb_post ][ :brand_list ] = ADMIN_CHANNEL if params[ >> :bb_post ][ :brand_list ].blank? >> p params >> @bb_post = @feature.posts.new( params[ :bb_post ] ) >> @bb_post.user = current_user >> # CREATE Channel Blog post >> respond_to do |format| >> if @bb_post.save >> flash[ :notice ] = ''Blog post was successfully created.'' >> format.html { redirect_to( blog_bb_posts_url ) } >> format.xml { render :xml => @bb_post, :status => :created, >> :location => @bb_post } >> else >> load_featured_posts if @bb_post.editorial? >> format.html { render :action => "new" } >> format.xml { render :xml => @bb_post.errors, :status => >> :unprocessable_entity } >> end >> end >> end >> >> Now if i run my specs,i get an error which says >> >> NoMethodError in ''BbPostsController POST Crete dmin should sve post'' >> You hve nil object when you didn''t expect it! >> The error occurred while evluting nil.posts >> Error comes at "@bb_post = @feature.posts.new( params[ :bb_post ] )" > > This tells you that @feature is nil. This is because filter that > declares @feature and assigns it a value is being stubbed. Although > very common, using filters to set instance variables makes it > difficult to test in isolation. > > I recommend pushing the model concerns to the model, and use filters > for application flow control concerns (like authentication/ > authorization) in the controller. In this case, that would mean > eliminating the validate_channel, is_feature_active, and > load_categories filters and simply passing the params to the model. > I''m not sure of the models and relationships, but I''m imagining > something like: > > def create > @bb_post = BbPost.new( params[ :bb_post ].merge(:user => > current_user) ) > respond_to do |format| > if @bb_post.save > ... > > All of the logic in the filters and before the assignment of > @bb_post can be managed in the BbPost model, where it is far easier > to spec in my experience. > > If you don''t want to follow that recommendation, you''ll have to > either set up all the state you need in the database for each > example, or do more invasive setup like this: > > controller.instance_eval { @feature = mock(''feature'') }I''d just back this up to say that one of the major benefits of test- driven-development is the guidance you get with your design. Mocking, particularly, gives you really clear feedback on whether you have a loosely-coupled OO design or not. If you''re finding your specs are hard to write, and your mocks are unwieldy and complex, it''s always a good idea to take a step back and think about whether your design needs some work.> >> Just above that line i tried to print params and i get following >> values: >> #<BbPost:0x44d1cd8 @nme="BbPost_1001"> >> {"ction"=>"crete", "controller"=>"bb_posts", >> "bb_post"=>{"brnd_list"=>"BrndPotio >> n", "title"=>"test123"}} >> >> Can somebody help me with this >> Also by using controller.stub!(:validate_channel).and_return(true) >> can >> somebody tell me actually,what exactly happens here.How does it >> handle >> before filters. > > Filters are just methods that get called implicitly by the > controller. RSpec''s mocking framework doesn''t handle them in any > special way (nor does any test double frameowrk that I''m aware of). > Stubs return values. They do not set state on the object in > question, which is why if you stub is_feature_active, for example, > that needs to be coupled with code that sets the value of @feature > on the controller (per above). > > HTH, > David > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-userscheers, Matt http://mattwynne.net +447974 430184