Patrick J. Collins
2011-Nov-19 01:42 UTC
[rspec-users] testing a post to a controller''s create action
I just spent a lot of time trying to get a test to pass that would not pass no matter what I did, and I finally decided to just do something really simple to verify that even that was working-- and it''s not. class PostsController < ApplicationController def create debugger # or binding.pry end end -- #controllers/post_spec.rb describe PostsController do it "does not work" do post :create end end ... I never see the debugger prompt.. Can anyone PLEASE tell me why this is not working? In any other test, binding.pry or debugger interrupts the test flow and gives me access to the current scope of the debugger call. Patrick J. Collins http://collinatorstudios.com
David Chelimsky
2011-Nov-19 02:18 UTC
[rspec-users] testing a post to a controller''s create action
On Nov 18, 2011, at 7:42 PM, Patrick J. Collins wrote:> I just spent a lot of time trying to get a test to pass that would not pass no > matter what I did, and I finally decided to just do something really simple to > verify that even that was working-- and it''s not. > > class PostsController < ApplicationController > > def create > debugger # or binding.pry > end > > end > > -- > > #controllers/post_spec.rb > > describe PostsController do > > it "does not work" do > post :create > end > > end > > ... > > I never see the debugger prompt.. Can anyone PLEASE tell me why this is not > working? In any other test, binding.pry or debugger interrupts the test flow > and gives me access to the current scope of the debugger call.Got any authentication in front of posts#create?
Mike Mazur
2011-Nov-19 04:50 UTC
[rspec-users] testing a post to a controller''s create action
Hi, On Sat, Nov 19, 2011 at 09:42, Patrick J. Collins <patrick at collinatorstudios.com> wrote:> I just spent a lot of time trying to get a test to pass that would not pass no > matter what I did, and I finally decided to just do something really simple to > verify that even that was working-- and it''s not. > > class PostsController < ApplicationController > > ? ? ? ?def create > ? ? ? ? ? ? ? ?debugger # or binding.pry > ? ? ? ?end > > end > > -- > > #controllers/post_spec.rbShouldn''t this be called `spec/controllers/posts_controller_spec.rb`?> describe PostsController do > > ? ? ? ?it "does not work" do > ? ? ? ? ? ? ? ?post :create > ? ? ? ?end > > end > > ... > > I never see the debugger prompt.. ?Can anyone PLEASE tell me why this is not > working? ?In any other test, binding.pry or debugger interrupts the test flow > and gives me access to the current scope of the debugger call.Also as David suggests, perhaps it''s a before_filter somewhere sending a response before the request is passed to PostsController#create. Mike
Patrick J. Collins
2011-Nov-19 16:57 UTC
[rspec-users] testing a post to a controller''s create action
> > I never see the debugger prompt.. Can anyone PLEASE tell me why this is not > > working? In any other test, binding.pry or debugger interrupts the test flow > > and gives me access to the current scope of the debugger call. > > Got any authentication in front of posts#create?Yeah, that was the problem! There was authentication in the ApplicationController and so I needed a skip_before_filter... Ok.. Taking this a step forward, what I really am trying to do is write tests that isolate some methods that are called by before_filters in my controller. So, lets say I have something like: class PostsController < ApplicationController before_filter :store_params, :only => :create def create ... create stuff end def store_params session[:post_params] = params[:post] end end What I would like to do is totally isolate store_params... So I''d do something like: describe "#store_params" do it "saves the post params for later" do session[:post_params].should be_blank post :create, { :post => { :fake_param => "foobar" } } session[:post_params][:fake_param].should == "foobar" end end ... Ok this is all groovy, except my tests will fail because fake_param is not an attribute on Post, and when it actually gets to the create method it essentially be trying to do Post.create!(:fake_param => "foobar")... So obviously I could choose to go about this differently and use a real post attribute-- but I am just curious if there is a way to isolate the testing of this method and make #create not even connected in any way? I was thinking originally that I should be able to do (prior to the post create call): PostsController.any_instance.stubs(:create).returns true But that doesn''t seem to do anything... Patrick J. Collins http://collinatorstudios.com
Justin Ko
2011-Nov-19 19:40 UTC
[rspec-users] testing a post to a controller''s create action
On Nov 19, 2011, at 9:57 AM, Patrick J. Collins wrote:>>> I never see the debugger prompt.. Can anyone PLEASE tell me why this is not >>> working? In any other test, binding.pry or debugger interrupts the test flow >>> and gives me access to the current scope of the debugger call. >> >> Got any authentication in front of posts#create? > > Yeah, that was the problem! There was authentication in the > ApplicationController and so I needed a skip_before_filter... > > Ok.. Taking this a step forward, what I really am trying to do is write > tests that isolate some methods that are called by before_filters in my > controller. > > So, lets say I have something like: > > class PostsController < ApplicationController > > before_filter :store_params, :only => :create > > def create > ... create stuff > end > > def store_params > session[:post_params] = params[:post] > end > > end > > What I would like to do is totally isolate store_params... So I''d do something > like: > > describe "#store_params" do > it "saves the post params for later" do > session[:post_params].should be_blank > > post :create, { :post => { :fake_param => "foobar" } } > > session[:post_params][:fake_param].should == "foobar" > end > end#store_params is not an "action", therefore I would make it private. Since it is used only by the #create action, I would spec it as part of the #create spec: describe ''#create'' do it ''stores the :post params'' do post :create, { :post => { :fake_param => "foobar" } } session[:post_params][:fake_param].should == "foobar" end end> > ... Ok this is all groovy, except my tests will fail because fake_param is not > an attribute on Post, and when it actually gets to the create method it > essentially be trying to do Post.create!(:fake_param => "foobar")... So > obviously I could choose to go about this differently and use a real post > attribute-- but I am just curious if there is a way to isolate the testing of > this method and make #create not even connected in any way? > > I was thinking originally that I should be able to do (prior to the post create > call):You can, but you should mock Post.create!: describe ''#create'' do it ''stores the :post params'' do Post.should_receive(:create!).and_return(true) post :create, { :post => { :fake_param => "foobar" } } session[:post_params][:fake_param].should == "foobar" end end> > PostsController.any_instance.stubs(:create).returns true > > But that doesn''t seem to do anything... > > > Patrick J. Collins > http://collinatorstudios.com > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
Patrick J. Collins
2011-Nov-19 23:03 UTC
[rspec-users] testing a post to a controller''s create action
> #store_params is not an "action", therefore I would make it private.I agree completely, and probably should have specified that in my sample code (it certainly is private in my real non-hypothetical implementation).> Since it is used only by the #create action, I would spec it as part of the #create spec:Well this came up for me because I have a bit of a complex controller which has several before_filters, and I wanted a clean way to really verify that each and every filter is doing what it should, so that''s why I wanted to spec those individual methods.> You can, but you should mock Post.create!: > > describe ''#create'' do > it ''stores the :post params'' do > Post.should_receive(:create!).and_return(true) > post :create, { :post => { :fake_param => "foobar" } } > session[:post_params][:fake_param].should == "foobar" > end > endUnfortunately, this project is using a plugin called "Decent Exposure" which does various magic to simplify controller code (and honestly has been a big headache). So, the create action actually calls post.save! the ''post'' that .save! is called on is actually auto-generated by a method created by Decent Exposure. It returns a new record pre-populated with params[:post]... The failure I see is: Failure/Error: post :create, { :post => { :fake_param => "foobar" } } ActiveRecord::UnknownAttributeError: unknown attribute: foo # ./app/controllers/posts_controller.rb:107:in `new'' ... So, I can''t stub out "create!", because this failure is happening before there... And, if I do: Post.stubs(:new).returns(true) I still get the same failure. I even took it a step further where I explicitly told decent exposure to use a method: expose(:post) do exposed_for_post if params[:post] end def exposed_for_post Post.new(params[:post]) end .. And guess what? Doing subject.stubs(:exposed_for_post).returns true still gives me the same failure, unknown attribute: foo in ''new''... But, manually testing stuff proves that exposed_for_post method IS being called, and everything is working.. I just want an automated test to prove it. I am totally stumped on this one... Patrick J. Collins http://collinatorstudios.com
Justin Ko
2011-Nov-20 05:49 UTC
[rspec-users] testing a post to a controller''s create action
On Nov 19, 2011, at 4:03 PM, Patrick J. Collins wrote:>> #store_params is not an "action", therefore I would make it private. > > I agree completely, and probably should have specified that in my sample code > (it certainly is private in my real non-hypothetical implementation). > >> Since it is used only by the #create action, I would spec it as part of the #create spec: > > Well this came up for me because I have a bit of a complex controller which has > several before_filters, and I wanted a clean way to really verify that each and > every filter is doing what it should, so that''s why I wanted to spec those > individual methods. > >> You can, but you should mock Post.create!: >> >> describe ''#create'' do >> it ''stores the :post params'' do >> Post.should_receive(:create!).and_return(true) >> post :create, { :post => { :fake_param => "foobar" } } >> session[:post_params][:fake_param].should == "foobar" >> end >> end > > Unfortunately, this project is using a plugin called "Decent Exposure" which > does various magic to simplify controller code (and honestly has been a big > headache). So, the create action actually calls post.save! > > the ''post'' that .save! is called on is actually auto-generated by a method > created by Decent Exposure. It returns a new record pre-populated with > params[:post]... > > The failure I see is: > > Failure/Error: post :create, { :post => { :fake_param => "foobar" } } > ActiveRecord::UnknownAttributeError: > unknown attribute: foo > # ./app/controllers/posts_controller.rb:107:in `new'' > > > ... So, I can''t stub out "create!", because this failure is happening before > there... And, if I do: Post.stubs(:new).returns(true) > > I still get the same failure. > > I even took it a step further where I explicitly told decent exposure to use a > method: > > expose(:post) do > exposed_for_post if params[:post] > end > > def exposed_for_post > Post.new(params[:post]) > end > > .. And guess what? Doing subject.stubs(:exposed_for_post).returns true > > still gives me the same failure, unknown attribute: foo in ''new''... But, > manually testing stuff proves that exposed_for_post method IS being called, and > everything is working.. I just want an automated test to prove it. > > I am totally stumped on this one... > > Patrick J. Collins > http://collinatorstudios.com > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersPlease post the backtrace.
Patrick J. Collins
2011-Nov-20 06:14 UTC
[rspec-users] testing a post to a controller''s create action
> Please post the backtrace.Failures: 1) PostsController#store_post_params stores the last post params in the session Failure/Error: post :create, { :submit_action => submit_type.to_s, :post => { :foo => "bar" } } ActiveRecord::UnknownAttributeError: unknown attribute: foo # ./app/controllers/posts_controller.rb:107:in `new'' # ./app/controllers/posts_controller.rb:107:in `exposed_for_session'' # ./app/controllers/posts_controller.rb:9 # ./app/controllers/posts_controller.rb:37:in `create'' # ./spec/controllers/post_spec.rb:6:in `do_post'' # ./spec/controllers/post_spec.rb:25 ------------- and exposed_for_session is: def exposed_for_session Post.new(session.delete(:last_post_params)) if session[:last_post_params] end Like I said, I tried stubbing out the new class method on post by doing something like: fake_post = stub(''Post'', :save => true) Post.stubs(:new).returns(fake_post) But I still get that same failure. Patrick J. Collins http://collinatorstudios.com
Justin Ko
2011-Nov-20 07:58 UTC
[rspec-users] testing a post to a controller''s create action
On Nov 19, 2011, at 11:14 PM, Patrick J. Collins wrote:>> Please post the backtrace. > > Failures: > > 1) PostsController#store_post_params stores the last post params in the session > Failure/Error: post :create, { :submit_action => submit_type.to_s, :post => { :foo => "bar" } } > ActiveRecord::UnknownAttributeError: > unknown attribute: foo > # ./app/controllers/posts_controller.rb:107:in `new'' > # ./app/controllers/posts_controller.rb:107:in `exposed_for_session'' > # ./app/controllers/posts_controller.rb:9 > # ./app/controllers/posts_controller.rb:37:in `create'' > # ./spec/controllers/post_spec.rb:6:in `do_post'' > # ./spec/controllers/post_spec.rb:25 > > ------------- > > and exposed_for_session is: > > def exposed_for_session > Post.new(session.delete(:last_post_params)) if session[:last_post_params] > end > > Like I said, I tried stubbing out the new class method on post by doing something like: > > fake_post = stub(''Post'', :save => true) > Post.stubs(:new).returns(fake_post) > > But I still get that same failure. > > Patrick J. Collins > http://collinatorstudios.com > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersI haven''t used ActiveRecord in quite awhile (been using MongoDB), but it looks like you cannot instantiate a record with attributes that don''t exist. I think you have two options here: 1.) Only use valid attributes in your params. 2.) Add `with` to your stub to exactly match the arguments to `.new`: Post.stubs(:new).with({last_post_params: {foo: ''bar''}}).returns(fake_post)
Patrick J. Collins
2011-Nov-20 08:46 UTC
[rspec-users] testing a post to a controller''s create action
> I haven''t used ActiveRecord in quite awhile (been using MongoDB), but it looks like you cannot instantiate a record with attributes that don''t exist. I think you have two options here: > > 1.) Only use valid attributes in your params. > 2.) Add `with` to your stub to exactly match the arguments to `.new`: > Post.stubs(:new).with({last_post_params: {foo: ''bar''}}).returns(fake_post)Yeah.. I originally had tried your 2nd option... Putting an expectation on the arguments sent to new made no difference. I just tried it again to verify, and I still get the same error. Seems so weird to me. Patrick J. Collins http://collinatorstudios.com
David Chelimsky
2011-Nov-20 12:41 UTC
[rspec-users] testing a post to a controller''s create action
On Nov 20, 2011, at 2:46 AM, Patrick J. Collins wrote:>> I haven''t used ActiveRecord in quite awhile (been using MongoDB), but it looks like you cannot instantiate a record with attributes that don''t exist. I think you have two options here: >> >> 1.) Only use valid attributes in your params. >> 2.) Add `with` to your stub to exactly match the arguments to `.new`: >> Post.stubs(:new).with({last_post_params: {foo: ''bar''}}).returns(fake_post) > > Yeah.. I originally had tried your 2nd option... Putting an expectation on > the arguments sent to new made no difference. I just tried it again to verify, > and I still get the same error. Seems so weird to me.Then use the first! The potential problem is that the params change and you need to change them here, but you can alleviate that with a method like valid_attributes: describe PostsController do def valid_attributes { :title => "Isolating change" } end describe "#store_params" do it "saves the post params for later" do session[:post_params].should be_blank post :create, { :post => valid_attributes } session[:post_params].should eq(valid_attributes) end end end You could also use FactoryGirl.attributes_for(:post) if you''re using that tool. Either way, this removes the need to figure out where to stub what on the model. If you''re concerned about the DB call, you could still stub save! but using valid_attributes would get you past the validations that happen before save!. HTH, David