[rspec-users] nested blocks

David Chelimsky dchelimsky at gmail.com
Tue May 15 14:49:25 EDT 2007


On 5/15/07, Chris Hoffman <chris.c.hoffman at gmail.com> wrote:
> Hello,
>
> Does anyone have an explanation for why the following code results in
> the error that follows?  I also would value any criticisms of my
> approach (i.e., defining it_should_populate_collections).  Thanks
>
> -Chris
>
> #Code
>
> def it_should_populate_collections(condition=nil)
>   it "should populate any necessary collections #{condition}" do
>     [*@collections_to_populate].each do |collection|
>       collection_model = collection.to_s.classify.constantize
>       collection_model.should_receive(:find).with(:all).and_return("the
> #{collection}")
>     end
>     yield
>     [*@collections_to_populate].each do |collection|
>       assigns[collection].should == "the #{collection}"
>     end
>   end
> end
>
> describe "Foo" do
>   before(:all) do
>     @collections_to_populate = :bars
>   end
>
>   before(:each) do
>     @obj = Foo.new
>   end
>
>   it_should_populate_collections "on a save failure" do
>     @obj.should_receive(:save)
>     post @action
>   end
> end
>
> #Error
>
> undefined method `post' for #<Spec::DSL::EvalModule:0xb6e7b2e0>

post is only available in controller specs, identified by living in
spec/controllers or by saying:

  describe "Foo", :behaviour_type => :controller do

My guess is that you're doing this in spec/models, but that's just a
guess (please include complete error messages instead of just one line
when you post questions like this).

As for the approach:

1. I'd recommend only using before(:all) for expensive operations like
initializing databases. In this example, there is no benefit to using
before(:all). Not even a runtime benefit, because any instance vars
that you set up get copied around which is no less expensive than
simply re-creating them before(:each) example. Using before(:all) and
before(:each) together just makes things confusing.

2. "it_should_populate_collections" speaks very nicely from a
documentation standpoint, but from a failure isolation standpoint
(i.e. understanding quickly why something fails when it does) I think
it's a bit problematic. There's just no way to understand what's going
on in the example without looking at the definition of
it_should_populate_collections. It's very clever, but takes attention
away from where it should be. The fact that it yields back to the
current block makes it even more confusing. I'd stay away from that in
specs.

In general, I'd encourage you to use custom matchers rather than
adding "it_blah" methods (i.e. @model.should populate_collections(:a,
:b) ). I think that will make it much easier for other people to look
at your specs and understand them from a documentation perspective,
and a failure isolation perspective.

2 cents

Cheers,
David


More information about the rspec-users mailing list