[rspec-devel] [ rspec-Feature Requests-8126 ] Nested specify blocks

noreply at rubyforge.org noreply at rubyforge.org
Sun Feb 4 17:29:10 EST 2007


Feature Requests item #8126, was opened at 2007-01-25 12:25
You can respond by visiting: 
http://rubyforge.org/tracker/?func=detail&atid=3152&aid=8126&group_id=797

Category: expectation module
Group: None
Status: Open
Priority: 3
Submitted By: Wincent Colaiuta (wincent)
Assigned to: Nobody (None)
Summary: Nested specify blocks

Initial Comment:
I know that the idea of nested contexts has come up on the devel list before (Google for "pipermail rspec nested contexts") and most were not in favor of the idea.

For me, more useful than nested contexts would be nested specifications ("specify" blocks). Currently I have specs which look like this:

context 'outer' do
  specify 'inner' do
    this.should == that
  end
end

If a spec is not satisfied then rspec prints a message of the form "outer inner". I sometimes find myself wishing that I could nest the specify blocks to fine tune the failure message:

context 'outer' do
  specify 'inner' do
    specify 'innermost' do
      this.should == that
    end
  end
end

In this case rspec would print "outer inner innermost" on failure. Let me flesh it out a bit with more of a concrete example; basically I often find myself wanting to write this:

context 'applying a regexp to a string' do
  specify 'should be able to match "zero or more" times' do
    specify 'with input that matches zero times' do
      # test zero-match case
    end
    specify 'with input that matches one time' do
      # test one-match case
    end
    specify 'with input that matches two times' do
      # test two-match case
    end      
  end
end

Because I can't be bothered writing this:

context 'applying a regexp to a string' do
  specify 'should be able to match "zero or more" times (with input that matches zero times)' do
    # test zero-match case
  end
  specify 'should be able to match "zero or more" times (with input that matches one time)' do
    # test one-match case
  end
  specify 'should be able to match "zero or more" times  (with input that matches two times)' do
    # test two-match case
  end      
end

But I end up writing this:

context 'applying a regexp to a string' do
  specify 'should be able to match "zero or more" times' do
    # test zero-match case
    # test one-match case
    # test two-match case
  end      
end

This isn't ideal because if the first assertion fails then the others aren't even tested. I'd rather keep the number of assertions in each specify block a bit lower, and the ability to nest specify blocks would help me to do that. What do people think?

----------------------------------------------------------------------

Comment By: Lachie Cox (lachie)
Date: 2007-02-05 09:29

Message:
I this given-when-then by different names?

----------------------------------------------------------------------

Comment By: David Chelimsky (dchelimsky)
Date: 2007-02-05 09:21

Message:
Thinking about this some more - I think that there are many structures that could prove useful. We've named "context/specify/example", but there is also "story/scenario" for acceptance level spec'ing, "behaviour/specify/example" when the context is not so much a system state, but rather an aspect of behaviour like "error messages".

This is leading me to this thought: what if the structure was simply a tree?

We could have a method #rspec_node(*args, &block) that returns an RSpecNode. This would be implemented in Kernel, aliased with names like #context, #behaviour and #story. It would also be implemented in RSpecNode, aliased with names like #specify, #example and #scenario.

Nested nodes would have access to state in any of its ancestor nodes, supporting the long sought-after "nested context", but siblings would not have access to each other's state.

Command line options could load a file which defines custom aliases that you want to use that don't ship with RSpec, so you could things like "project/iteration/story/scenario" if that made you happy.

I've taken a look at this and it would require a lot of work, but there's no reason it shouldn't be 100% backwards compatible (except for monkey patching), and it's something we can probably introduce gradually, starting w/ nested specify blocks, and working up to unifying Context and Specification into a single RSpecNode.

WDYT of that?

----------------------------------------------------------------------

Comment By: Wincent Colaiuta (wincent)
Date: 2007-01-28 03:42

Message:
Yes, I think that "context/specify/example" would be a great 
way to split things up, and the language you've proposed 
maps well onto the behaviour-driven way of thinking about 
software development ("in this context ... this should 
happen ... example ...").

About the only thing I would change in your example is that 
I would start each example with the word "for" so that when 
the three strings are chained together they would read like, 
"applying a regexp to a string should be able to match 'zero 
or more' times for the zero-match case" etc:

context 'applying a regexp to a string' do
  specify 'should be able to match "zero or more" times' do
    example 'for the zero-match case' do ... end
    example 'for the one-match case' do ... end
    example 'for the two-match case' do ... end
  end      
end

This has the added benefit that the line introducing each 
example in the specification reads as "example for zero-
match case" etc, which is highly readable and descriptive.

----------------------------------------------------------------------

Comment By: David Chelimsky (dchelimsky)
Date: 2007-01-28 02:53

Message:
I love this idea, but I think nesting specify would be confusing to look at. How about this:

context
  specify
    example

context 'applying a regexp to a string' do
  specify 'should be able to match "zero or more" times' do
    example "zero-match case" do
    example "one-match case" do
    example "two-match case" do
  end      
end

----------------------------------------------------------------------

You can respond by visiting: 
http://rubyforge.org/tracker/?func=detail&atid=3152&aid=8126&group_id=797


More information about the rspec-devel mailing list