Can I construct the controller myself in a controller spec?


#1

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?


#2

On Wed, Mar 4, 2009 at 10:38 PM, Perryn F. removed_email_address@domain.invalid
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-enterprise.html, in
which Jamis B., 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


#3

On Wed, Mar 4, 2009 at 8:38 PM, Perryn F. removed_email_address@domain.invalid
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
removed_email_address@domain.invalid
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


#4

On 5 Mar 2009, at 06:02, David C. 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-enterprise.html, in
which Jamis B., 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 W.
http://blog.mattwynne.net
http://www.songkick.com


#5

On Thu, Mar 5, 2009 at 3:31 AM, Matt W. removed_email_address@domain.invalid 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


#6

David C. 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


#7

On Thu, Mar 5, 2009 at 9:24 AM, Ben M. removed_email_address@domain.invalid 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


#8

On Fri, Mar 6, 2009 at 3:05 AM, Matt W. removed_email_address@domain.invalid 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 D.
http://www.continuousthinking.com
http://www.mutuallyhuman.com


#9

On 5 Mar 2009, at 15:24, Ben M. 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 W.
http://blog.mattwynne.net
http://www.songkick.com