Jonathan del Strother
2007-Feb-06 13:19 UTC
[mocha-developer] Login systems : stubbing accounts and AR association proxies
My Rails site has a fair amount of login and ''ownership'' logic. For instance, we have a number of clients (companies), each of which has several accounts. A client owns a number of different types of resources, and shouldn''t see any other clients'' resources, so, for example, our ScenesController contains a lot of references to ''current_client.scenes'', rather than Scene.find(:all) All of which works pretty well, but it can make testing very tedious. I always need to set up a fake client, and a fake account, and log in with that account, and make sure that whatever resource I''m dealing with in that unit test is assigned to the fake client, and so on. I''m starting to wonder whether I ought to stub all the login and ownership logic out of my functional tests, so I can concentrate on whatever resource I''m dealing with in that given test. All the login/ ownership stuff could be handled in integration testing, which is probably better suited to it anyway, due to having real sessions etc etc. I''m having difficulty doing so, however. The controller uses some current_account / current_client methods to find whoever''s logged in - stubbing those to return a fake client/account is easy, but you''re then left with problems with Rails'' association proxies. There''s lots of use of things like current_client.scenes.build, which are not so easy to stub out (particularly as, AFAICT, you can''t make use of any arguments passed into a stubbed method). Has anyone else done anything similar, or am I just creating more work for myself by trying to remove login details from functional testing? Jon
James Mead
2007-Feb-06 14:33 UTC
[mocha-developer] Login systems : stubbing accounts and AR association proxies
On 06/02/07, Jonathan del Strother <maillist at steelskies.com> wrote:> > My Rails site has a fair amount of login and ''ownership'' logic. For > instance, we have a number of clients (companies), each of which has > several accounts. A client owns a number of different types of > resources, and shouldn''t see any other clients'' resources, so, for > example, our ScenesController contains a lot of references to > ''current_client.scenes'', rather than Scene.find(:all) > > > All of which works pretty well, but it can make testing very > tedious. I always need to set up a fake client, and a fake account, > and log in with that account, and make sure that whatever resource > I''m dealing with in that unit test is assigned to the fake client, > and so on. > I''m starting to wonder whether I ought to stub all the login and > ownership logic out of my functional tests, so I can concentrate on > whatever resource I''m dealing with in that given test. All the login/ > ownership stuff could be handled in integration testing, which is > probably better suited to it anyway, due to having real sessions etc > etc. > > I''m having difficulty doing so, however. The controller uses some > current_account / current_client methods to find whoever''s logged in > - stubbing those to return a fake client/account is easy, but you''re > then left with problems with Rails'' association proxies. There''s > lots of use of things like current_client.scenes.build, which are > not so easy to stub out (particularly as, AFAICT, you can''t make use > of any arguments passed into a stubbed method). > > > Has anyone else done anything similar, or am I just creating more > work for myself by trying to remove login details from functional > testing? >I think your motivation is good. We have run into similar issues with association proxies. Rails association proxies encourage train-wreck code / violations of the Law of Demeter (http://en.wikipedia.org/wiki/Law_of_Demeter). We''ve tended to introduce public methods on the parent class which delegate to their proxies. Then you can then stub the newly introduced methods. So in your case a simple version would be... class Client < ActiveRecord::Base has_many :scenes def build_scene scenes.build end end Then you can stub Client#build_scene. My colleague Chris Roos has some thoughts on this subject here... http://blog.seagul.co.uk/articles/2007/01/12/encapsulation-in-active-record-objects -- James. http://blog.floehopper.org
Zack Chandler
2007-Feb-06 15:43 UTC
[mocha-developer] Login systems : stubbing accounts and AR association proxies
On 2/6/07, Jonathan del Strother <maillist at steelskies.com> wrote:> My Rails site has a fair amount of login and ''ownership'' logic. For > instance, we have a number of clients (companies), each of which has > several accounts. A client owns a number of different types of > resources, and shouldn''t see any other clients'' resources, so, for > example, our ScenesController contains a lot of references to > ''current_client.scenes'', rather than Scene.find(:all) > > > All of which works pretty well, but it can make testing very > tedious. I always need to set up a fake client, and a fake account, > and log in with that account, and make sure that whatever resource > I''m dealing with in that unit test is assigned to the fake client, > and so on. > I''m starting to wonder whether I ought to stub all the login and > ownership logic out of my functional tests, so I can concentrate on > whatever resource I''m dealing with in that given test. All the login/ > ownership stuff could be handled in integration testing, which is > probably better suited to it anyway, due to having real sessions etc > etc. > > I''m having difficulty doing so, however. The controller uses some > current_account / current_client methods to find whoever''s logged in > - stubbing those to return a fake client/account is easy, but you''re > then left with problems with Rails'' association proxies. There''s > lots of use of things like current_client.scenes.build, which are > not so easy to stub out (particularly as, AFAICT, you can''t make use > of any arguments passed into a stubbed method). > > > Has anyone else done anything similar, or am I just creating more > work for myself by trying to remove login details from functional > testing? > > > JonI''ve noticed too that association proxies are hard to mock up. Here''s an example of how I have done it: In your controller: def some_action @tasks = current_user.tasks.find(:all, :conditions => [...]) end In your functional test: User.any_instance.stubs(:tasks).returns(Task) Task.stubs(:find).returns([Task.new(...), Task.new(...)]) I would think their is a better way. For one thing this hard-codes the return values up so any call with any conditions would return the same array of tasks. Still it is super-simple and works in common cases. I''d be interested to hear about better ways to do this. -- Zack Chandler http://depixelate.com