This is a rehash of a question I posed at: http://stackoverflow.com/questions/9952317/isolating-controller-tests-from-models The basic question: If I have a FoosController and a Foo model, can I run FoosController rspec tests without creating the foos database table? I haven''t found a good way to avoid it. Consider this simple controller: # file: app/controllers/foos_controller.rb class FoosController < ApplicationController respond_to :json def create @foo = Foo.create(params[:foo]) respond_with @foo end end In my RSpec tests, I can do @foo = mock_model("Foo") Foo.stub(:create) { @foo } post :create, :format => :json ... but that fails in the call to respond_with @foo, because ActionController calls @foo.has_errors? which in turn hits the database to look up column names. I could stub out has_errors?, but that means that I''m reaching into the internals of the system -- probably not a good idea for a test suite. If the answer to my question "can I do controller testing without creating the underlying database tables?" is ''no'', then I''ll accept that (with regret). But I''d rather hear that it is somehow possible. - ff -- Posted via http://www.ruby-forum.com/.
On Mar 30, 2012, at 11:11 PM, Fearless Fool wrote:> This is a rehash of a question I posed at: > http://stackoverflow.com/questions/9952317/isolating-controller-tests-from-models > The basic question: If I have a FoosController and a Foo model, can I > run FoosController rspec tests without creating the foos database table? > I haven''t found a good way to avoid it. > > Consider this simple controller: > > # file: app/controllers/foos_controller.rb > class FoosController < ApplicationController > respond_to :json > def create > @foo = Foo.create(params[:foo]) > respond_with @foo > end > end > > In my RSpec tests, I can do > > @foo = mock_model("Foo") > Foo.stub(:create) { @foo } > post :create, :format => :json > > ... but that fails in the call to respond_with @foo, because > ActionController calls @foo.has_errors? which in turn hits the database > to look up column names. I could stub out has_errors?, but that means > that I''m reaching into the internals of the system -- probably not a > good idea for a test suite.Oh, the irony! You''re using rspec-rails, which reaches *much further* into the "system" than `has_errors?`. Mucking with a non-public API is not a "never ever" thing :)> > If the answer to my question "can I do controller testing without > creating the underlying database tables?" is ''no'', then I''ll accept that > (with regret). But I''d rather hear that it is somehow possible. > > - ff > > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
David Chelimsky
2012-Mar-31 11:50 UTC
[rspec-users] isolating controller tests from models
On Sat, Mar 31, 2012 at 2:49 AM, Justin Ko <jko170 at gmail.com> wrote:> > On Mar 30, 2012, at 11:11 PM, Fearless Fool wrote: > >> This is a rehash of a question I posed at: >> http://stackoverflow.com/questions/9952317/isolating-controller-tests-from-models >> The basic question: If I have a FoosController and a Foo model, can I >> run FoosController rspec tests without creating the foos database table? >> I haven''t found a good way to avoid it. >> >> Consider this simple controller: >> >> ? # file: app/controllers/foos_controller.rb >> ? class FoosController < ApplicationController >> ? ? respond_to :json >> ? ? def create >> ? ? ? @foo = Foo.create(params[:foo]) >> ? ? ? respond_with @foo >> ? ? end >> ? end >> >> In my RSpec tests, I can do >> >> ? @foo = mock_model("Foo") >> ? Foo.stub(:create) { @foo } >> ? post :create, :format => :json >> >> ... but that fails in the call to respond_with @foo, because >> ActionController calls @foo.has_errors? which in turn hits the database >> to look up column names.mock_model is not an ActiveRecord object, so if it''s hitting the db then something''s not wired up correctly.> I could stub out has_errors?, but that means >> that I''m reaching into the internals of the system -- probably not a >> good idea for a test suite. > > Oh, the irony! > > You''re using rspec-rails, which reaches *much further* into the "system" than `has_errors?`.There''s a difference between a library doing this and end-user specs doing this. They both bear risk, but the burden is on the library developer (and in one place) if it''s the lib, whereas its on all end users who stray on their own.> Mucking with a non-public API is not a "never ever" thing :)Care to elaborate on what the risks are and when you think it''s worth it?>> If the answer to my question "can I do controller testing without >> creating the underlying database tables?" is ''no'', then I''ll accept that >> (with regret). ?But I''d rather hear that it is somehow possible.Of course its possible - just a matter of whether its worth it. This relates to an earlier question which I believe you posted about why the generated controller specs changed for rails 3. respond_with is another addition to rails 3 that tightly couples controllers to models, making it more challenging to stub models in controller specs. If you''re going to violate the "mock your code, not other people''s" guideline, you might find it safer to do it at a higher level of abstraction: mock respond_with directly on the controller. Still a violation of stub/mock guidelines, but at least it deals with code that you can see and change in the controller. HTH, David
Sent from my iPhone On Mar 31, 2012, at 5:50 AM, David Chelimsky <dchelimsky at gmail.com> wrote:> On Sat, Mar 31, 2012 at 2:49 AM, Justin Ko <jko170 at gmail.com> wrote: >> >> On Mar 30, 2012, at 11:11 PM, Fearless Fool wrote: >> >>> This is a rehash of a question I posed at: >>> http://stackoverflow.com/questions/9952317/isolating-controller-tests-from-models >>> The basic question: If I have a FoosController and a Foo model, can I >>> run FoosController rspec tests without creating the foos database table? >>> I haven''t found a good way to avoid it. >>> >>> Consider this simple controller: >>> >>> # file: app/controllers/foos_controller.rb >>> class FoosController < ApplicationController >>> respond_to :json >>> def create >>> @foo = Foo.create(params[:foo]) >>> respond_with @foo >>> end >>> end >>> >>> In my RSpec tests, I can do >>> >>> @foo = mock_model("Foo") >>> Foo.stub(:create) { @foo } >>> post :create, :format => :json >>> >>> ... but that fails in the call to respond_with @foo, because >>> ActionController calls @foo.has_errors? which in turn hits the database >>> to look up column names. > > mock_model is not an ActiveRecord object, so if it''s hitting the db > then something''s not wired up correctly. > >> I could stub out has_errors?, but that means >>> that I''m reaching into the internals of the system -- probably not a >>> good idea for a test suite. >> >> Oh, the irony! >> >> You''re using rspec-rails, which reaches *much further* into the "system" than `has_errors?`. > > There''s a difference between a library doing this and end-user specs > doing this. They both bear risk, but the burden is on the library > developer (and in one place) if it''s the lib, whereas its on all end > users who stray on their own. > >> Mucking with a non-public API is not a "never ever" thing :) > > Care to elaborate on what the risks are and when you think it''s worth it?The risk is you might be changing the behaviour of rails, which means you''re not testing the true behaviour of the system. That goes with the public API too, but you''re less likely to screw that up, and is less brittle. In this case, if there is no way around mocking ''has_errors?'', I think it''s worth the benefit of not hitting the database. I wouldn''t go past mocking more than one very simple internal method, that''s just asking for trouble. It''s all about trade offs, right?> >>> If the answer to my question "can I do controller testing without >>> creating the underlying database tables?" is ''no'', then I''ll accept that >>> (with regret). But I''d rather hear that it is somehow possible. > > Of course its possible - just a matter of whether its worth it. This > relates to an earlier question which I believe you posted about why > the generated controller specs changed for rails 3. respond_with is > another addition to rails 3 that tightly couples controllers to > models, making it more challenging to stub models in controller specs. > If you''re going to violate the "mock your code, not other people''s" > guideline, you might find it safer to do it at a higher level of > abstraction: mock respond_with directly on the controller. Still a > violation of stub/mock guidelines, but at least it deals with code > that you can see and change in the controller.OP, since you basically need to load the rails env for controller specs, I dont see a problem with hitting the DB to check columns. Usually, the downside to hitting the DB is performance, but as long as you''re not querying/creating/deleting records there''s no downside to it IMO.> > HTH, > David > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users