[rspec-users] DRYer controller specs

nicholas a. evans
2007-04-11T14:07:12Z

So, I''ve been following the recommendations for controller specs here:
http://blog.davidchelimsky.net/articles/2006/11/09/tutorial-rspec-stubs-and-mocks

Most notably: a single expectation per specify block; the setup block
contains only stubs; mock expectations each get their own specify
block.  (I''m still using 0.8, so I haven''t gotten the
describe/it
goodness yet.)

I don''t really mind the stub/mock duplication (between setup and the
specifications).  What''s annoying me is the action duplication.
"get
''create''" is called in each specify block, sometimes
before the actual
specification, sometimes afterward, depending on whether it''s a mock
expectation or a state expectation.

So I played around with creating the following module and then
extending my context with it.

module MockSpecHelpers
  def expect_that_it(msg)
    specify msg do
      yield
      action
    end
  end
  def then_it(msg)
    specify msg do
      action
      yield
    end
  end
end

in the context block, I would then "def action do get
''create'' end".
And I would replace the following specify blocks

  specify "should create a new person on GET to create" do
    Person.should_receive(:new).and_return(@person)
    get ''create''
  end

  specify "should assign new person to template on GET to create" do
    get ''create''
    assigns[:person].should_be @person
  end

with

  expect_that_it "should create a new person on GET to create" do
    Person.should_receive(:new).and_return(@person)
  end

  then_it "should assign new person to template on GET to create" do
    assigns[:person].should_be @person
  end

The problem is that my weak Ruby metaprogramming fu has completely
failed me.  :-)  I can''t figure out how to get the block to be
evaluated in a context that has access to the appropriate context
methods (assigns, response, session, etc).  I get errors like the
following:

NameError in ''GET /controller/action/1234 should render the foobar
template''
undefined local variable or method `response'' for
#<Spec::Runner::ContextEvalModule:0xb6def95c>

(Usually the "expect_that_it" blocks work fine, because they
aren''t
dependent on anything that is specific to their EvalContext.)

Does someone know how I could get this working?

I also thought about making an alias for setup called "given" and
instead of "action" it could be "when_action" to more
closely follow
the "given/expect/when/then" pattern and naming conventions.

Does this even look like a good idea, or is there a better way?  Would
it make more sense to open the EvalContext rather than trying to
extend a module?

--
Nick