[rspec-devel] [ rspec-Feature Requests-8126 ] Nested specify blocks
noreply at rubyforge.org
noreply at rubyforge.org
Mon Feb 5 05:12:21 EST 2007
Feature Requests item #8126, was opened at 2007-01-25 01: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: Wincent Colaiuta (wincent)
Date: 2007-02-05 10:12
Message:
> 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.
Above all, I think *some kind* of nesting support would be a
wonderful improvement in terms of readability/expressiveness
and also in terms of saved typing; these are the reasons why
I filed the feature request.
I am also sure that the ability to basically twist RSpec
into any shape you want and use any terminology you want
would be popular.
BUT I also think that it might be a bit dangerous to give
people too much free reign. One of the things about BDD in
general and specifically RSpec is that the language that it
imposes ("context"/"specify") helps developers to do the
right thing and follow best testing practice. If people
could use any terminology at all then some of that benefit
might be lost. Also, in the same way that overuse of C
preprocessor macros can make it harder to read other
people's source code, if the terminology for specifying
behaviour is made too flexible then it might become more
difficult to read other people's specs.
So I'm not saying that you shouldn't do it, but that if you
do decide to go down this path I think your idea of doing so
in an incremental fashion is a good one; it will give time
to really think about it and be sure of why/how/if it should
be done.
I like the idea of using "story" for acceptance level
testing. I am not sure whether "behaviour" is the best term
for cases where "the context is not
so much a system state, but rather an aspect of behaviour";
I think the word that you used right there, "aspect", is
probably a better choice. That's just my initial gut
feeling; I'd need to think about it more to be sure.
----------------------------------------------------------------------
Comment By: Lachie Cox (lachie)
Date: 2007-02-04 22:29
Message:
I this given-when-then by different names?
----------------------------------------------------------------------
Comment By: David Chelimsky (dchelimsky)
Date: 2007-02-04 22: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-27 16: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-27 15: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