Perryn Fowler
2009-Mar-05 04:38 UTC
[rspec-users] Can I construct the controller myself in a controller spec?
Hi I am experimenting with Constructor based dependency injection for rails controllers. So I have something like this class LoginSessionsController < ApplicationController def initialize(authenticator = TheRealAuthenticator) @authenticator = authenticator end .... end The plan was to override the authenticator used when testing with something like describe LoginSessionsController.new(MyMockAuthenticator) do ...... end but rspec seems to insist on constructing the controller itself Is there a way to do this I am missing?
David Chelimsky
2009-Mar-05 06:02 UTC
[rspec-users] Can I construct the controller myself in a controller spec?
On Wed, Mar 4, 2009 at 10:38 PM, Perryn Fowler <pezlists at gmail.com> wrote:> Hi > > I am experimenting with Constructor based dependency injection for > rails controllers. > > So I have something like this > > class LoginSessionsController < ApplicationController > > ?def initialize(authenticator = ?TheRealAuthenticator) > ? ?@authenticator = authenticator > ?end > > ?.... > end > > > The plan was to override the authenticator used when testing with something like > > describe LoginSessionsController.new(MyMockAuthenticator) do > ? ...... > end > > but rspec seems to insist on constructing the controller itselfNo, Rails insists upon constructing the controller itself. rspec-rails just wraps rails'' testing framework. I''d much rather be constructing the objects myself. As for DI, there is a wealth of discussion about DI in Ruby in various user lists - general consensus is don''t do it. I concur. You don''t need it. You might be particularly interested in http://rubyconf2008.confreaks.com/recovering-from-enterprise.html, in which Jamis Buck, who authored a DI library for Ruby named needle, explains why that was unnecessary. But even if you disagree, you''re kinda stuck here because Rails wants to construct the controllers for us. HTH, David> > Is there a way to do this I am missing? > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Pat Maddox
2009-Mar-05 06:47 UTC
[rspec-users] Can I construct the controller myself in a controller spec?
On Wed, Mar 4, 2009 at 8:38 PM, Perryn Fowler <pezlists at gmail.com> wrote:> Hi > > I am experimenting with Constructor based dependency injection for > rails controllers. > > So I have something like this > > class LoginSessionsController < ApplicationController > > ?def initialize(authenticator = ?TheRealAuthenticator) > ? ?@authenticator = authenticator > ?end > > ?.... > end > > > The plan was to override the authenticator used when testing with something like > > describe LoginSessionsController.new(MyMockAuthenticator) do > ? ...... > end > > but rspec seems to insist on constructing the controller itself > > Is there a way to do this I am missing? > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >It''d be badass, but, basically, no. Instantiating a controller in Rails takes a lot of work - request/response objects, routes, and the code that does so is a big mess. You could use factory methods, and then stub that method in your examples: class LoginSessionsController < ApplicationController def authenticator TheRealAuthenticator end end describe LoginSessionsController do before(:each) do controller.stub!(:authenticator).and_return MockAuthenticator end end Pat
Matt Wynne
2009-Mar-05 09:31 UTC
[rspec-users] Can I construct the controller myself in a controller spec?
On 5 Mar 2009, at 06:02, David Chelimsky wrote:> On Wed, Mar 4, 2009 at 10:38 PM, Perryn Fowler <pezlists at gmail.com> > wrote: >> Hi >> >> I am experimenting with Constructor based dependency injection for >> rails controllers. >> >> So I have something like this >> >> class LoginSessionsController < ApplicationController >> >> def initialize(authenticator = TheRealAuthenticator) >> @authenticator = authenticator >> end >> >> .... >> end >> >> >> The plan was to override the authenticator used when testing with >> something like >> >> describe LoginSessionsController.new(MyMockAuthenticator) do >> ...... >> end >> >> but rspec seems to insist on constructing the controller itself > > No, Rails insists upon constructing the controller itself. rspec-rails > just wraps rails'' testing framework. I''d much rather be constructing > the objects myself. > > As for DI, there is a wealth of discussion about DI in Ruby in various > user lists - general consensus is don''t do it. I concur. You don''t > need it. You might be particularly interested in > http://rubyconf2008.confreaks.com/recovering-from-enterprise.html, in > which Jamis Buck, who authored a DI library for Ruby named needle, > explains why that was unnecessary.That sounds a little bit like throwing the baby out with the bathwater though - you still need to be able to do some kind of DI if you''re doing TDD, so that you can swap in a fake dependency, no? DI doesn''t have to mean you use some fancy framework. As Pat said, this is hard when the object you want to test (in this case, a Rails ActionController) is so damn awkward to instantiate in a test. One approach would be to change the references to @authenticator in your controller to call a local #authenticator method, then override this with a TestController subclass and use that subclass instead in your test. David, how would you suggest the OP injects his MockAuthenticator? stub out Authenticator.new? I guess that''s what I mostly do. Matt Wynne http://blog.mattwynne.net http://www.songkick.com
David Chelimsky
2009-Mar-05 14:26 UTC
[rspec-users] Can I construct the controller myself in a controller spec?
On Thu, Mar 5, 2009 at 3:31 AM, Matt Wynne <matt at mattwynne.net> wrote:> > On 5 Mar 2009, at 06:02, David Chelimsky wrote: > >> On Wed, Mar 4, 2009 at 10:38 PM, Perryn Fowler <pezlists at gmail.com> wrote: >>> >>> Hi >>> >>> I am experimenting with Constructor based dependency injection for >>> rails controllers. >>> >>> So I have something like this >>> >>> class LoginSessionsController < ApplicationController >>> >>> ?def initialize(authenticator = ?TheRealAuthenticator) >>> ? @authenticator = authenticator >>> ?end >>> >>> ?.... >>> end >>> >>> >>> The plan was to override the authenticator used when testing with >>> something like >>> >>> describe LoginSessionsController.new(MyMockAuthenticator) do >>> ?...... >>> end >>> >>> but rspec seems to insist on constructing the controller itself >> >> No, Rails insists upon constructing the controller itself. rspec-rails >> just wraps rails'' testing framework. I''d much rather be constructing >> the objects myself. >> >> As for DI, there is a wealth of discussion about DI in Ruby in various >> user lists - general consensus is don''t do it. I concur. You don''t >> need it. You might be particularly interested in >> http://rubyconf2008.confreaks.com/recovering-from-enterprise.html, in >> which Jamis Buck, who authored a DI library for Ruby named needle, >> explains why that was unnecessary. > > That sounds a little bit like throwing the baby out with the bathwater > though - you still need to be able to do some kind of DI if you''re doing > TDD, so that you can swap in a fake dependency, no? DI doesn''t have to mean > you use some fancy framework.Agreed. I think I reacted to the term DI, about which J.B. Rainsberger once commented "Oh, you mean parameters." I totally agree that we need strategies for introducing doubles.> > As Pat said, this is hard when the object you want to test (in this case, a > Rails ActionController) is so damn awkward to instantiate in a test. > > One approach would be to change the references to @authenticator in your > controller to call a local #authenticator method, then override this with a > TestController subclass and use that subclass instead in your test. > > David, how would you suggest the OP injects his MockAuthenticator? stub out > Authenticator.new? I guess that''s what I mostly do.That''s what I usually do as well. If you think of it, in this case Authenticator is the factory, and you''re providing a stub factory with this approach - something that would require more elaborate measures in other languages. Maybe we should make this easier by providing some facility in the mock framework to express the following in one statement: @authenticator = stub(''authenticator'') Authenticator.stub!(:new).and_return(@authenticator) Sure, you could make that a one liner: Authenticator.stub!(:new).and_return(@authenticator = stub(''authenticator'') But I mean something like: @authenticator = Authenticator.stub! I don''t think *that* is the answer - but something that concise would be nice. Thoughts? David> > Matt Wynne > http://blog.mattwynne.net > http://www.songkick.com > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Ben Mabey
2009-Mar-05 15:24 UTC
[rspec-users] Can I construct the controller myself in a controller spec?
David Chelimsky wrote:> On Thu, Mar 5, 2009 at 3:31 AM, Matt Wynne <matt at mattwynne.net> wrote: > >> On 5 Mar 2009, at 06:02, David Chelimsky wrote: >> >> >>> On Wed, Mar 4, 2009 at 10:38 PM, Perryn Fowler <pezlists at gmail.com> wrote: >>> >>>> Hi >>>> >>>> I am experimenting with Constructor based dependency injection for >>>> rails controllers. >>>> >>>> So I have something like this >>>> >>>> class LoginSessionsController < ApplicationController >>>> >>>> def initialize(authenticator = TheRealAuthenticator) >>>> @authenticator = authenticator >>>> end >>>> >>>> .... >>>> end >>>> >>>> >>>> The plan was to override the authenticator used when testing with >>>> something like >>>> >>>> describe LoginSessionsController.new(MyMockAuthenticator) do >>>> ...... >>>> end >>>> >>>> but rspec seems to insist on constructing the controller itself >>>> >>> No, Rails insists upon constructing the controller itself. rspec-rails >>> just wraps rails'' testing framework. I''d much rather be constructing >>> the objects myself. >>> >>> As for DI, there is a wealth of discussion about DI in Ruby in various >>> user lists - general consensus is don''t do it. I concur. You don''t >>> need it. You might be particularly interested in >>> http://rubyconf2008.confreaks.com/recovering-from-enterprise.html, in >>> which Jamis Buck, who authored a DI library for Ruby named needle, >>> explains why that was unnecessary. >>> >> That sounds a little bit like throwing the baby out with the bathwater >> though - you still need to be able to do some kind of DI if you''re doing >> TDD, so that you can swap in a fake dependency, no? DI doesn''t have to mean >> you use some fancy framework. >> > > Agreed. I think I reacted to the term DI, about which J.B. Rainsberger > once commented "Oh, you mean parameters." I totally agree that we need > strategies for introducing doubles. > > >> As Pat said, this is hard when the object you want to test (in this case, a >> Rails ActionController) is so damn awkward to instantiate in a test. >> >> One approach would be to change the references to @authenticator in your >> controller to call a local #authenticator method, then override this with a >> TestController subclass and use that subclass instead in your test. >> >> David, how would you suggest the OP injects his MockAuthenticator? stub out >> Authenticator.new? I guess that''s what I mostly do. >> > > That''s what I usually do as well. If you think of it, in this case > Authenticator is the factory, and you''re providing a stub factory with > this approach - something that would require more elaborate measures > in other languages. > > Maybe we should make this easier by providing some facility in the > mock framework to express the following in one statement: > > @authenticator = stub(''authenticator'') > Authenticator.stub!(:new).and_return(@authenticator) > > Sure, you could make that a one liner: > > Authenticator.stub!(:new).and_return(@authenticator = stub(''authenticator'') > > But I mean something like: > > @authenticator = Authenticator.stub! > > I don''t think *that* is the answer - but something that concise would be nice. > > Thoughts? > > David > >I like the conciseness, but it isn''t very clear what it is doing IMO. Perhaps something a little more intention-revealing like: @authenticator = Authenticator.stub_new! -Ben
David Chelimsky
2009-Mar-05 17:39 UTC
[rspec-users] Can I construct the controller myself in a controller spec?
On Thu, Mar 5, 2009 at 9:24 AM, Ben Mabey <ben at benmabey.com> wrote:> David Chelimsky wrote: >> >> On Thu, Mar 5, 2009 at 3:31 AM, Matt Wynne <matt at mattwynne.net> wrote: >> >>> >>> On 5 Mar 2009, at 06:02, David Chelimsky wrote: >>> >>> >>>> >>>> On Wed, Mar 4, 2009 at 10:38 PM, Perryn Fowler <pezlists at gmail.com> >>>> wrote: >>>> >>>>> >>>>> Hi >>>>> >>>>> I am experimenting with Constructor based dependency injection for >>>>> rails controllers. >>>>> >>>>> So I have something like this >>>>> >>>>> class LoginSessionsController < ApplicationController >>>>> >>>>> ?def initialize(authenticator = ?TheRealAuthenticator) >>>>> ?@authenticator = authenticator >>>>> ?end >>>>> >>>>> ?.... >>>>> end >>>>> >>>>> >>>>> The plan was to override the authenticator used when testing with >>>>> something like >>>>> >>>>> describe LoginSessionsController.new(MyMockAuthenticator) do >>>>> ?...... >>>>> end >>>>> >>>>> but rspec seems to insist on constructing the controller itself >>>>> >>>> >>>> No, Rails insists upon constructing the controller itself. rspec-rails >>>> just wraps rails'' testing framework. I''d much rather be constructing >>>> the objects myself. >>>> >>>> As for DI, there is a wealth of discussion about DI in Ruby in various >>>> user lists - general consensus is don''t do it. I concur. You don''t >>>> need it. You might be particularly interested in >>>> http://rubyconf2008.confreaks.com/recovering-from-enterprise.html, in >>>> which Jamis Buck, who authored a DI library for Ruby named needle, >>>> explains why that was unnecessary. >>>> >>> >>> That sounds a little bit like throwing the baby out with the bathwater >>> though - you still need to be able to do some kind of DI if you''re doing >>> TDD, so that you can swap in a fake dependency, no? DI doesn''t have to >>> mean >>> you use some fancy framework. >>> >> >> Agreed. I think I reacted to the term DI, about which J.B. Rainsberger >> once commented "Oh, you mean parameters." I totally agree that we need >> strategies for introducing doubles. >> >> >>> >>> As Pat said, this is hard when the object you want to test (in this case, >>> a >>> Rails ActionController) is so damn awkward to instantiate in a test. >>> >>> One approach would be to change the references to @authenticator in your >>> controller to call a local #authenticator method, then override this with >>> a >>> TestController subclass and use that subclass instead in your test. >>> >>> David, how would you suggest the OP injects his MockAuthenticator? stub >>> out >>> Authenticator.new? I guess that''s what I mostly do. >>> >> >> That''s what I usually do as well. If you think of it, in this case >> Authenticator is the factory, and you''re providing a stub factory with >> this approach - something that would require more elaborate measures >> in other languages. >> >> Maybe we should make this easier by providing some facility in the >> mock framework to express the following in one statement: >> >> @authenticator = stub(''authenticator'') >> Authenticator.stub!(:new).and_return(@authenticator) >> >> Sure, you could make that a one liner: >> >> Authenticator.stub!(:new).and_return(@authenticator >> stub(''authenticator'') >> >> But I mean something like: >> >> @authenticator = Authenticator.stub! >> >> I don''t think *that* is the answer - but something that concise would be >> nice. >> >> Thoughts? >> >> David >> >> > > I like the conciseness, but it isn''t very clear what it is doing IMO. > ?Perhaps something a little more intention-revealing like: > > @authenticator = Authenticator.stub_new!That''s the right direction. Not in love with that either - especially since it requires adding a new method to Object. How about something like: stub_factory(Authenticator) This would configure Authenticator to return the same test double every time it receives #new. So you could say: stub_factory(Authenticator) @authenticator = Authenticator.new Or, perhaps: @authenticator = stub_factory(Authenticator).new Not there yet (that''s a bit confusing), but I think you can see where this is headed. More ideas? David> > -Ben > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Matt Wynne
2009-Mar-06 08:05 UTC
[rspec-users] Can I construct the controller myself in a controller spec?
On 5 Mar 2009, at 15:24, Ben Mabey wrote:> David Chelimsky wrote: >> On Thu, Mar 5, 2009 at 3:31 AM, Matt Wynne <matt at mattwynne.net> >> wrote: >> Maybe we should make this easier by providing some facility in the >> mock framework to express the following in one statement: >> >> @authenticator = stub(''authenticator'') >> Authenticator.stub!(:new).and_return(@authenticator) >> >> Sure, you could make that a one liner: >> >> Authenticator.stub!(:new).and_return(@authenticator = >> stub(''authenticator'') >> >> But I mean something like: >> >> @authenticator = Authenticator.stub! >> >> I don''t think *that* is the answer - but something that concise >> would be nice. >> >> Thoughts? >> >> David >> >> > I like the conciseness, but it isn''t very clear what it is doing > IMO. Perhaps something a little more intention-revealing like: > > @authenticator = Authenticator.stub_new!I think I must mostly use constructor injection, as I don''t really seem to have a pattern for this, but if I did I guess it would be something like this: extend StubbingHelpers describe "blah" before(:each) do @authenticator = mock_new_authenticator( :foo => bar ) which is implemented like def stub_new_authenticator( *args ) result = mock(Authenticator, *args) Authenticator.stub!(:new).and_return(result) result end I guess having something like that would be nice. Matt Wynne http://blog.mattwynne.net http://www.songkick.com
Zach Dennis
2009-Mar-06 16:24 UTC
[rspec-users] Can I construct the controller myself in a controller spec?
On Fri, Mar 6, 2009 at 3:05 AM, Matt Wynne <matt at mattwynne.net> wrote:> > On 5 Mar 2009, at 15:24, Ben Mabey wrote: > >> David Chelimsky wrote: >>> >>> On Thu, Mar 5, 2009 at 3:31 AM, Matt Wynne <matt at mattwynne.net> wrote: >>> Maybe we should make this easier by providing some facility in the >>> mock framework to express the following in one statement: >>> >>> @authenticator = stub(''authenticator'') >>> Authenticator.stub!(:new).and_return(@authenticator) >>> >>> Sure, you could make that a one liner: >>> >>> Authenticator.stub!(:new).and_return(@authenticator >>> stub(''authenticator'') >>> >>> But I mean something like: >>> >>> @authenticator = Authenticator.stub! >>> >>> I don''t think *that* is the answer - but something that concise would be >>> nice. >>> >>> Thoughts? >>> >>> David >>> >>> >> I like the conciseness, but it isn''t very clear what it is doing IMO. >> ?Perhaps something a little more intention-revealing like: >> >> @authenticator = Authenticator.stub_new! > > I think I must mostly use constructor injection, as I don''t really seem to > have a pattern for this, but if I did I guess it would be something like > this: > > ? ?extend StubbingHelpers > > ? ?describe "blah" > ? ? ?before(:each) do > ? ? ? ?@authenticator = mock_new_authenticator( :foo => bar ) > > which is implemented like > > ? ?def stub_new_authenticator( *args ) > ? ? ?result = mock(Authenticator, *args) > ? ? ?Authenticator.stub!(:new).and_return(result) > ? ? ?result > ? ?end > > I guess having something like that would be nice.This is how I do it, Zach> > > Matt Wynne > http://blog.mattwynne.net > http://www.songkick.com > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- Zach Dennis http://www.continuousthinking.com http://www.mutuallyhuman.com