Forum: RSpec Can I construct the controller myself in a controller spec?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B7522c0680a645c365deae239b7af6fc?d=identicon&s=25 Perryn Fowler (Guest)
on 2009-03-05 06:39
(Received via mailing list)
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?
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-03-05 07:42
(Received via mailing list)
On Wed, Mar 4, 2009 at 10:38 PM, Perryn Fowler <pezlists@gmail.com>
wrote:
>    @authenticator = authenticator
> 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-..., 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
39100495c9937c39b2e0c704444e1b4a?d=identicon&s=25 Pat Maddox (Guest)
on 2009-03-05 08:01
(Received via mailing list)
On Wed, Mar 4, 2009 at 8:38 PM, Perryn Fowler <pezlists@gmail.com>
wrote:
>    @authenticator = authenticator
> 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@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
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2009-03-05 10:57
(Received via mailing list)
On 5 Mar 2009, at 06:02, David Chelimsky wrote:

>>
>>
> 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-..., 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
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-03-05 15:58
(Received via mailing list)
On Thu, Mar 5, 2009 at 3:31 AM, Matt Wynne <matt@mattwynne.net> wrote:
>>> So I have something like this
>>>
>> just wraps rails' testing framework. I'd much rather be constructing
> 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
C694a032be7518a0d704318895f8fe1d?d=identicon&s=25 Ben Mabey (mabes)
on 2009-03-05 16:52
(Received via mailing list)
David Chelimsky wrote:
>>>> rails controllers.
>>>> end
>>>>
>>>
>
>
>
> 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
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2009-03-05 19:07
(Received via mailing list)
On Thu, Mar 5, 2009 at 9:24 AM, Ben Mabey <ben@benmabey.com> wrote:
>>>> wrote:
>>>>>
>>>>>
>>>>
>>> TDD, so that you can swap in a fake dependency, no? DI doesn't have to
>>> As Pat said, this is hard when the object you want to test (in this case,
>>> Authenticator.new? I guess that's what I mostly do.
>> @authenticator = stub('authenticator')
>>
>  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
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2009-03-06 09:40
(Received via mailing list)
On 5 Mar 2009, at 15:24, Ben Mabey wrote:

>>
>> 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
F86901feca747abbb5c6c020362ef2e7?d=identicon&s=25 Zach Dennis (zdennis)
on 2009-03-06 18:44
(Received via mailing list)
On Fri, Mar 6, 2009 at 3:05 AM, Matt Wynne <matt@mattwynne.net> wrote:
>>> Authenticator.stub!(:new).and_return(@authenticator)
>>> I don't think *that* is the answer - but something that concise would be
>> @authenticator = Authenticator.stub_new!
>
> 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


>
--
Zach Dennis
http://www.continuousthinking.com
http://www.mutuallyhuman.com
This topic is locked and can not be replied to.