I am trying to specify a controller with RSpec for the first time. I have been using mock_model to date but, given that the models are fully implemented, I am wondering if I should be using stub_model. Based on what I have read, I am struggling to understand the advantages that stub_model over mock_model might provide. Should I be using stub_model and if so, why? Thanks. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20090519/03f2cbdb/attachment.html>
On 19 May 2009, at 14:53, Lee Longmore wrote:> I am trying to specify a controller with RSpec for the first time. > > I have been using mock_model to date but, given that the models are > fully implemented, I am wondering if I should be using stub_model. > > Based on what I have read, I am struggling to understand the > advantages that stub_model over mock_model might provide. > > Should I be using stub_model and if so, why?*Should* is a big word here. It depends. When you use a stub model, you get a real instance of your AR model object, with the database connection crippled so you can''t accidentally do something that will make your specs slow. The benefit of this is that you get all the methods on the model available to the controller. For example, if you have a User class which takes a date_of_birth attribute in it''s constructor, you can call the #age method. This can make your tests easier to read. The cost of this is that your controller specs will be covering code in the model classes as well as the controller you''re explicitly testing. There is a chance you''ll make a change to the #age method on your User (maybe you want to specify in days rather than years), make the user specs pass, and check in believing you''re done. Unwittingly, you have just broken the controller specs too. If you read what from the really experienced guys who started this stuff off[1] say, you''ll hear that mocks are really a design tool. By creating a mock_model in your controller specs rather than leaning on the stub_model, you''re designing your ''ideal model'', rather than being constrained by whatever default methods activerecord gives you, or you have already written. I often find that this process drives out much more elegant interfaces onto the models, and sometimes even shows me where I need to introduce an intermediate class. I think this is something you really have to play around with for yourself to find the right balance for you and your team. Matt Wynne http://blog.mattwynne.net http://www.songkick.com
On 19 May 2009, at 18:11, Matt Wynne wrote:> > On 19 May 2009, at 14:53, Lee Longmore wrote: > >> I am trying to specify a controller with RSpec for the first time. >> >> I have been using mock_model to date but, given that the models are >> fully implemented, I am wondering if I should be using stub_model. >> >> Based on what I have read, I am struggling to understand the >> advantages that stub_model over mock_model might provide. >> >> Should I be using stub_model and if so, why? > > *Should* is a big word here. It depends. > > When you use a stub model, you get a real instance of your AR model > object, with the database connection crippled so you can''t > accidentally do something that will make your specs slow. The > benefit of this is that you get all the methods on the model > available to the controller. For example, if you have a User class > which takes a date_of_birth attribute in it''s constructor, you can > call the #age method. > > This can make your tests easier to read. > > The cost of this is that your controller specs will be covering code > in the model classes as well as the controller you''re explicitly > testing. There is a chance you''ll make a change to the #age method > on your User (maybe you want to specify in days rather than years), > make the user specs pass, and check in believing you''re done. > Unwittingly, you have just broken the controller specs too. > > If you read what from the really experienced guys who started this > stuff off[1] say, you''ll hear that mocks are really a design tool. > By creating a mock_model in your controller specs rather than > leaning on the stub_model, you''re designing your ''ideal model'', > rather than being constrained by whatever default methods > activerecord gives you, or you have already written. I often find > that this process drives out much more elegant interfaces onto the > models, and sometimes even shows me where I need to introduce an > intermediate class. > > I think this is something you really have to play around with for > yourself to find the right balance for you and your team. > > Matt Wynne > http://blog.mattwynne.net > http://www.songkick.com[1]http://www.m3p.co.uk/blog/2009/03/08/mock-roles-not-objects-live-and-in-person/ Matt Wynne http://blog.mattwynne.net http://www.songkick.com
Thanks Matt. Very helpful. I have tried out stub_model. The constructor of one of my models (Namespace) requires a mandatory :name attribute to be provided. So when I try to stub_model it - e.g. representative_namespace stub_model(Namespace) - I get the following spec error. 1) RuntimeError in ''ContextsController POST create authenticated user should assign a representative namespace to the new context'' A namespace must be assigned a valid name /Users/leelon/work/cbox/app/models/namespace.rb:696:in `raise_no_name_error'' /Users/leelon/work/cbox/app/models/namespace.rb:688:in `get_processed_name'' /Users/leelon/work/cbox/app/models/namespace.rb:643:in `set_name'' /Users/leelon/work/cbox/app/models/namespace.rb:64:in `initialize'' ./spec/controllers/contexts_controller_spec.rb:37: script/spec:10: Is there a way around this while still making :name a mandatory requirement of the constructor? I have tried stubbing out :name in the call to stub_model but this doesn''t help: representative_namespace = stub_model(Namespace, :name => ''A name'') Thanks.
On 20 May 2009, at 12:03, Lee wrote:> Thanks Matt. Very helpful. > > I have tried out stub_model. The constructor of one of my models > (Namespace) requires a mandatory :name attribute to be provided. So > when I try to stub_model it - e.g. representative_namespace > stub_model(Namespace) - I get the following spec error. > > 1) > RuntimeError in ''ContextsController POST create authenticated user > should assign a representative namespace to the new context'' > A namespace must be assigned a valid name > /Users/leelon/work/cbox/app/models/namespace.rb:696:in > `raise_no_name_error'' > /Users/leelon/work/cbox/app/models/namespace.rb:688:in > `get_processed_name'' > /Users/leelon/work/cbox/app/models/namespace.rb:643:in `set_name'' > /Users/leelon/work/cbox/app/models/namespace.rb:64:in `initialize'' > ./spec/controllers/contexts_controller_spec.rb:37: > script/spec:10: > > Is there a way around this while still making :name a mandatory > requirement of the constructor? I have tried stubbing out :name in the > call to stub_model but this doesn''t help: > > representative_namespace = stub_model(Namespace, :name => ''A name'')I can''t remember if you can do that, to be honest. I think the hash of parameters to stub_model is used to set up stubs on the object - so they won''t get passed to the constructor. Hopefully someone else on the list should be able to tell you if this is possible. Matt Wynne http://blog.mattwynne.net http://www.songkick.com
> I can''t remember if you can do that, to be honest. I think the hash of > parameters to stub_model is used to set up stubs on the object - so > they won''t get passed to the constructor. Hopefully someone else on > the list should be able to tell you if this is possible.Matt, I have since looked at the source for stub_model on RSpec''s RDoc and I think you''re right and it''s not possible. I guess one option would be to fallback to mock_model for this model.