Testing a rails controller *outside* of spec/controllers

We have an app that has “extensions” that are built as rails engines.
Each engine needs some code in the controllers, which we solved as
“include this module, call a method”. In order to test it, though, we
don’t want to “copy” the test for how that works into each engine, so
the idea is to write the tests once, in ‘spec/lib/extensions_spec.rb’,
where we are testing the other stuff that’s common for all the
extensions (which are defined on lib/extensions.rb, thus the spec’s
location)

In order to do it, I’m declaring a ‘sample’ controller in the specs
(just include the module, call the method), and then writing a couple
specs for what we are doing. Problem is, I can’t get rspec to
understand that that example group is a controller example groups.

I have

class MyExampleController < ApplicationController
  include Extensions::ControllerHelpers
  call_awesome_method # this, among other things, defines an

instance method on the controller that I want to test
end

inside my spec, and after that I tried:

  1. describe(“controller helpers”, :type => :controller) {
    controller_name “my_example”; … }

It complains about controller_name not being a method of this dynamic
subclass of ActiveSupport::TestCase.
After some time reading the source I figured that :type => :controller
pretty much just works for config.include/config.extend. So on to the
next thing:

  1. describe MyExampleController do…end # aka, duh, the obvious one.

It complained about @controller being nil. Ended up reading most of
action_controller/test_case, and rspec-rails, with no success.

  1. move that outside the big top-level describe(Extensions). That at
    least got the specs running, but they failed because @controller is
    nil. Again. So “Ok, it’s not setting the controller class at any
    point, let’s force it”, and I added the ‘controller_name …’ call
    (also tried going low-level to calling
    ActiveSupport::TestCase.tests(klass), but that didn’t work either.)

  2. move all that into it’s own file (maybe, for some reason which
    wouldn’t make much sense, two different types of specs can’t live
    together in the same file)

Nope, same errors.

  1. move the file into spec/controllers

Bang! It works. But it’s ugly that it’s there, because I’m just
testing something “related” to controllers. (Granted, writing a
controller spec isn’t the best idea ever for this, but it’s the
straightest way to get a controller instanced and that responds to all
the routing helpers – which is, in the end, what I need to call.)

Basically, I read a lot of source code for both rspec-rails and rails’
own testing code, trying to figure out a way around of this, and I’m
running out of options. An alternative would be to include the module
and call the method on a mock instead of a real controller – but
‘call_awesome_method’ actually does some stuff with the rails router,
and I really don’t want to start writing all those huge mocks for just
testing a simple method (since the 2.3.x router is definitely not
something that I would characterize as ‘overly testable’)

So the question is: is there a way I force rspec into saying this is
a controller spec, treat it as such
when the describe block is not
defined on spec/controllers? And (Or) where does the relevant code to
decide if it’s a controller spec (i.e., it should subclass
ControllerExampleGroup) or otherwise live? I can manage to work my way
around from there :slight_smile:

Sorry for the rant, it’s past 4am here and I’m not sure I can think
straight at this anymore (or that all of this makes much sense)

Cheers,
-foca

On Mar 11, 2010, at 1:26 AM, Nicolás Sanguinetti wrote:

(just include the module, call the method), and then writing a couple

ActiveSupport::TestCase.tests(klass), but that didn’t work either.)
testing something “related” to controllers. (Granted, writing a
testing a simple method (since the 2.3.x router is definitely not
straight at this anymore (or that all of this makes much sense)
What version of rspec are you using?

Scott

On Mar 11, 2010, at 1:26 AM, Nicolás Sanguinetti wrote:

(just include the module, call the method), and then writing a couple

inside my spec, and after that I tried:

  1. describe(“controller helpers”, :type => :controller) {
    controller_name “my_example”; … }

It complains about controller_name not being a method of this dynamic
subclass of ActiveSupport::TestCase.

That seems like a really strange error. What is the actual stack trace?

What versions of the test-unit gem do you have installed? What version
of rails? rspec? If you uninstall the test-unit gem, will it work with
the :type => :controller hash given to describe?

(I’ve noticed very strange things with the test-unit gem installed in
the past - such as a test suite which was 100% green, using a hybrid of
mocha + rspec mocks/stubs. When the gem was uninstalled, half the test
suite failed!)

After some time reading the source I figured that :type => :controller
pretty much just works for config.include/config.extend. So on to the
next thing:

Have you taken a look at Spec::Example::ExampleGroupFactory?

Not sure if this will help, but here’s a patch that brynary and I banged
out way back in rspec 1.1.9 to support spec/unit & spec/integration:
(the usual disclaimer applies - all the good parts are his, all the bad
parts are mine)

That piece of the code might at least take you to a place where you can
debug what’s going on.

Best,

Scott

On Thu, Mar 11, 2010 at 5:10 AM, Scott T. [email protected]
wrote:

location)
call_awesome_method # this, among other things, defines an

That seems like a really strange error. What is the actual stack trace?

./spec/lib/extensions_spec.rb:20: undefined method controller_name' for ActiveSupport::TestCase::Subclass_1:Class (NoMethodError) from $gem_path/rspec-1.3.0/lib/spec/example/example_group_methods.rb:183:in module_eval’
from
$gem_path/rspec-1.3.0/lib/spec/example/example_group_methods.rb:183:in
subclass' from $gem_path/rspec-1.3.0/lib/spec/example/example_group_methods.rb:55:in describe’
from
$gem_path/rspec-1.3.0/lib/spec/example/example_group_factory.rb:31:in
create_example_group' from $gem_path/rspec-1.3.0/lib/spec/dsl/main.rb:28:in describe’
from ./spec/lib/extensions_spec.rb:19
from
$gem_path/activesupport-2.3.5/lib/active_support/dependencies.rb:147:in
load_without_new_constant_marking' from $gem_path/activesupport-2.3.5/lib/active_support/dependencies.rb:147:in load’
from
$gem_path/rspec-1.3.0/lib/spec/runner/example_group_runner.rb:15:in
load_files' from $gem_path/rspec-1.3.0/lib/spec/runner/example_group_runner.rb:14:in each’
from
$gem_path/rspec-1.3.0/lib/spec/runner/example_group_runner.rb:14:in
load_files' from $gem_path/rspec-1.3.0/lib/spec/runner/options.rb:133:in run_examples’
from $gem_path/rspec-1.3.0/lib/spec/runner/command_line.rb:9:in `run’
from script/spec:10

(sanitized it a bit)

What versions of the test-unit gem do you have installed? What version of rails? rspec? If you uninstall the test-unit gem, will it work with the :type => :controller hash given to describe?

This is using rails 2.3.5, rspec 1.3.0, and rspec-rails 1.3.2. I do
not have test-unit on this system, I have mocha 0.9.8 (though it
shouldn’t be required in the environment by anyone here)

(I’ve noticed very strange things with the test-unit gem installed in the past - such as a test suite which was 100% green, using a hybrid of mocha + rspec mocks/stubs. When the gem was uninstalled, half the test suite failed!)

After some time reading the source I figured that :type => :controller
pretty much just works for config.include/config.extend. So on to the
next thing:

Have you taken a look at Spec::Example::ExampleGroupFactory?

Ah, darn, I hadn’t gotten that far into rspec’s rabbit hole, and
missed the reference in rspec-rails. That’s what you get for trying to
debug stuff at 4am :slight_smile: So, :type => :controller should be enough, then.

Still, doing:

describe MyExampleController, :type => :controller do
it “should do stuff” do
controller.should_receive(:foo)
controller.extension.bar
end
end

Fails with ‘undefined local variable or method `controller’ for
#ActiveSupport::TestCase::Subclass_1::Subclass_4:0x1075ca230

And replacing controller with @controller gives me ‘undefined method
`bar’ for nil:NilClass’. Which is pretty much the same results I got a
few hours ago before going to sleep :frowning:

…So :type => :controller is apparently not enough for rspec to pick up
on this.

Not sure if this will help, but here’s a patch that brynary and I banged out way back in rspec 1.1.9 to support spec/unit & spec/integration: (the usual disclaimer applies - all the good parts are his, all the bad parts are mine)

foo.rb · GitHub

Thanks :slight_smile:

Interesting:

describe SomeModule do
describe “Bar”, :type => :controller do
controller_name “blah”
end
end

Doesn’t work, while

describe SomeModule do
end

describe “Bar”, :type => :controller do
controller_name “blah”
end

does.

2010/3/11 Nicolás Sanguinetti [email protected]:

2010/3/11 Nicolás Sanguinetti [email protected]:

Scratch that, it works if you run just that file, if you run the whole
suite it doesn’t. WTH.

The fact that it works at all is an accident :slight_smile:

Scratch that, it works if you run just that file, if you run the whole
suite it doesn’t. WTH.

2010/3/11 Nicolás Sanguinetti [email protected]:

On Thu, Mar 11, 2010 at 11:12 AM, David C. [email protected]
wrote:

2010/3/11 Nicolás Sanguinetti [email protected]:

Scratch that, it works if you run just that file, if you run the whole
suite it doesn’t. WTH.

The fact that it works at all is an accident :slight_smile:

Hah, ok :slight_smile:

Why shouldn’t two different types of specs work off the same file? (if
you care for a technical explanation :))

Since it works by moving it to another file, so I’m just doing that
(describe MyController, :type => :controller), so it’s just more idle
curiosity than anything else, at this point :slight_smile:

2010/3/11 Nicolás Sanguinetti [email protected]:

you care for a technical explanation :))
I explained that in another email this thread that I sent about a
minute before this one. Maybe google delivered them to you out of
order?

You can have different types in one file, but not in one nesting
construct. Nested example groups are subclasses of the outer group, so
it’s sort of like this:

class Outer < Spec::Example::ExampleGroup; end
class Inner < Outer

Obviously, things are going to get messy if you try to introduce a new
type to the mix on the way down the hierarchy.

Make sense?

On Thu, Mar 11, 2010 at 11:37 AM, David C. [email protected]
wrote:

Why shouldn’t two different types of specs work off the same file? (if
class Outer < Spec::Example::ExampleGroup; end
class Inner < Outer

Obviously, things are going to get messy if you try to introduce a new
type to the mix on the way down the hierarchy.

Ah, I thought that you were surprised at having the different types at
top-level on the same file passing with script/spec and failing with
rake spec – as in “it should always fail” – which is why I wanted an
explanation, since I didn’t see why.

I guess it’s some random setup thing in another part of the specs. In
any case, putting it in a different file makes it always pass (well,
as long as the code works :P)

Thanks for your time, both you and Scott ^^

2010/3/11 Nicolás Sanguinetti [email protected]:

Interesting:

describe SomeModule do
describe “Bar”, :type => :controller do
controller_name “blah”
end
end

You can only set the type in the outermost-nested group. The reason
for this is that the nested groups are subclasses of the outer group.

So you could do this:

describe SomeModule, :type => :controller do
describe “Bar” do
controller_name “blah”
end
end

and it should work.

HTH,
David