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