Hi all,
There are currently a number of ways to extend RSpec that are
documented inconsistently and work in sometimes conflicting and/or
confusing ways. I''d like to resolve that. The recent addition of
Spec::Runner.configure is part of that resolution.
The thing I want to tackle now is the stuff that happens before and
after each example. Currently (in trunk), things are executed in the
following order:
describe Thing do
before(:all) do
#happens first, but only once within this "describe" block
end
before(:each) do
#happens before each example within this "describe" block
end
it "should ..." do
#this is the example
end
after(:each) do
#happens after each example within this "describe" block
end
after(:all) do
#happens last, but only once within this "describe" block
end
end
In addition to this, there have been requests to support global
"before" and "after". Here''s where I''d
like to end up:
Spec::DSL::Example.before(:first) do
# before "before(:all)" and "before(:each)"
end
Spec::DSL::Example.before(:last) do
# after "before(:all)" and "before(:each)", but before the
example
end
Spec::DSL::Example.after(:first) do
# before "after(:each)" and "after(:all)", but after the
example
end
Spec::DSL::Example.after(:last) do
# after "after(:each)" and "after(:all)"
end
So the over all order for every example (a.k.a. specify) would be:
before(:first)
before(:all) #once per "describe"
before(:each)
before(:last)
example
after(:first)
after(:each)
after(:all) #once per "describe"
after(:last)
I''m certainly open to other suggestions - this is merely a proposal.
In most cases you wouldn''t use all of these. In fact, you may not use
any of them if you prefer the Object Mother approach (helper methods
that create the state you want to start with). But this would give you
a lot of choice at both global and granular levels.
WDYT? Too confusing? Too limiting? Just right?
Thanks,
David
> Hi all, > > There are currently a number of ways to extend RSpec that are > documented inconsistently and work in sometimes conflicting and/or > confusing ways. I''d like to resolve that. The recent addition of > Spec::Runner.configure is part of that resolution. > > The thing I want to tackle now is the stuff that happens before and > after each example. Currently (in trunk), things are executed in the > following order: > > describe Thing do > before(:all) do > #happens first, but only once within this "describe" block > end > before(:each) do > #happens before each example within this "describe" block > end > it "should ..." do > #this is the example > end > after(:each) do > #happens after each example within this "describe" block > end > after(:all) do > #happens last, but only once within this "describe" block > end > end > > In addition to this, there have been requests to support global > "before" and "after". Here''s where I''d like to end up: > > Spec::DSL::Example.before(:first) do > # before "before(:all)" and "before(:each)" > end > > Spec::DSL::Example.before(:last) do > # after "before(:all)" and "before(:each)", but before the example > end > > Spec::DSL::Example.after(:first) do > # before "after(:each)" and "after(:all)", but after the example > end > > Spec::DSL::Example.after(:last) do > # after "after(:each)" and "after(:all)" > end > > So the over all order for every example (a.k.a. specify) would be: > > before(:first) > before(:all) #once per "describe" > before(:each) > before(:last) > example > after(:first) > after(:each) > after(:all) #once per "describe" > after(:last) > > I''m certainly open to other suggestions - this is merely a proposal. > In most cases you wouldn''t use all of these. In fact, you may not use > any of them if you prefer the Object Mother approach (helper methods > that create the state you want to start with). But this would give you > a lot of choice at both global and granular levels. > > WDYT? Too confusing? Too limiting? Just right?It sounds good to me, although I can''t see a situation where I would need before(:last) or after(:first), just the outerlying ones (before (:first) and after(:last). Michael
On 4/16/07, Michael Trier <mtrier at eminentconsultinggroup.com> wrote:> > Hi all, > > > > There are currently a number of ways to extend RSpec that are > > documented inconsistently and work in sometimes conflicting and/or > > confusing ways. I''d like to resolve that. The recent addition of > > Spec::Runner.configure is part of that resolution. > > > > The thing I want to tackle now is the stuff that happens before and > > after each example. Currently (in trunk), things are executed in the > > following order: > > > > describe Thing do > > before(:all) do > > #happens first, but only once within this "describe" block > > end > > before(:each) do > > #happens before each example within this "describe" block > > end > > it "should ..." do > > #this is the example > > end > > after(:each) do > > #happens after each example within this "describe" block > > end > > after(:all) do > > #happens last, but only once within this "describe" block > > end > > end > > > > In addition to this, there have been requests to support global > > "before" and "after". Here''s where I''d like to end up: > > > > Spec::DSL::Example.before(:first) do > > # before "before(:all)" and "before(:each)" > > end > > > > Spec::DSL::Example.before(:last) do > > # after "before(:all)" and "before(:each)", but before the example > > end > > > > Spec::DSL::Example.after(:first) do > > # before "after(:each)" and "after(:all)", but after the example > > end > > > > Spec::DSL::Example.after(:last) do > > # after "after(:each)" and "after(:all)" > > end > > > > So the over all order for every example (a.k.a. specify) would be: > > > > before(:first) > > before(:all) #once per "describe" > > before(:each) > > before(:last) > > example > > after(:first) > > after(:each) > > after(:all) #once per "describe" > > after(:last) > > > > I''m certainly open to other suggestions - this is merely a proposal. > > In most cases you wouldn''t use all of these. In fact, you may not use > > any of them if you prefer the Object Mother approach (helper methods > > that create the state you want to start with). But this would give you > > a lot of choice at both global and granular levels. > > > > WDYT? Too confusing? Too limiting? Just right? > > It sounds good to me, although I can''t see a situation where I would > need before(:last) or after(:first), just the outerlying ones (before > (:first) and after(:last).The reason for before(:last) is a situation where you want to be able to set something up most of the time, but not all of the time. Take a look at my last comment on http://rubyforge.org/tracker/?group_id=797&atid=3152&func=detail&aid=9984. If that doesn''t clear it up for you, feel free to ask questions here. Thx, David> > Michael > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Dave and I have just had a lengthy chat about this and I think we''ve
come up with a pretty elegant solution for an API. It''s inspired from
the filter chaining mechanism in Rails.
Essentially you can specify several before and after blocks. This is
actually already supported in RSpec.
What we need is a mechanism that allows users to have more fine
grained control of the order in which
these blocks are executed, and we''re thinking of supporting this with
4 basic methods:
#prepend_before(where=:each, &proc)
#append_before(where=:each, &proc)
#prepend_after(where=:each, &proc)
#append_after(where=:each, &proc)
Further, #before will be an alias for #append_before and #after will
be an alias for #prepend_after.
This allows users to just write before{..} and after{..} if they don''t
care about the order.
This means that globally specified #before blocks (typically in a
spec_helper.rb via Spec::Runner#configure, which usually gets loaded
before everything else)
will be executed before any #before blocks in individual specs. The
opposite will be the case for #after blocks.
However, for those of you who wish to execute a #before block *before*
the global #before block, you can use #prepend_before
in the specs that want to do this.
Finally, the #before(:all) blocks will be executed before
#prepend_before(:each) blocks - the +where+ argument trumps the
prepend/append.
None of this is implemented yet, so please chime in soon if you have
any comments about this.
Aslak
On 4/16/07, David Chelimsky <dchelimsky at gmail.com>
wrote:> Hi all,
>
> There are currently a number of ways to extend RSpec that are
> documented inconsistently and work in sometimes conflicting and/or
> confusing ways. I''d like to resolve that. The recent addition of
> Spec::Runner.configure is part of that resolution.
>
> The thing I want to tackle now is the stuff that happens before and
> after each example. Currently (in trunk), things are executed in the
> following order:
>
> describe Thing do
> before(:all) do
> #happens first, but only once within this "describe" block
> end
> before(:each) do
> #happens before each example within this "describe" block
> end
> it "should ..." do
> #this is the example
> end
> after(:each) do
> #happens after each example within this "describe" block
> end
> after(:all) do
> #happens last, but only once within this "describe" block
> end
> end
>
> In addition to this, there have been requests to support global
> "before" and "after". Here''s where
I''d like to end up:
>
> Spec::DSL::Example.before(:first) do
> # before "before(:all)" and "before(:each)"
> end
>
> Spec::DSL::Example.before(:last) do
> # after "before(:all)" and "before(:each)", but
before the example
> end
>
> Spec::DSL::Example.after(:first) do
> # before "after(:each)" and "after(:all)", but after
the example
> end
>
> Spec::DSL::Example.after(:last) do
> # after "after(:each)" and "after(:all)"
> end
>
> So the over all order for every example (a.k.a. specify) would be:
>
> before(:first)
> before(:all) #once per "describe"
> before(:each)
> before(:last)
> example
> after(:first)
> after(:each)
> after(:all) #once per "describe"
> after(:last)
>
> I''m certainly open to other suggestions - this is merely a
proposal.
> In most cases you wouldn''t use all of these. In fact, you may not
use
> any of them if you prefer the Object Mother approach (helper methods
> that create the state you want to start with). But this would give you
> a lot of choice at both global and granular levels.
>
> WDYT? Too confusing? Too limiting? Just right?
>
> Thanks,
> David
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>
On 4/16/07, David Chelimsky <dchelimsky at gmail.com> wrote:> The thing I want to tackle now is the stuff that happens before and > after each example. Currently (in trunk), things are executed in the > following order:...> So the over all order for every example (a.k.a. specify) would be: > > before(:first) > before(:all) #once per "describe" > before(:each) > before(:last) > example > after(:first) > after(:each) > after(:all) #once per "describe" > after(:last) > > I''m certainly open to other suggestions - this is merely a proposal. > In most cases you wouldn''t use all of these....> WDYT? Too confusing? Too limiting? Just right?I usually would not have a need for any of them except for before(:each). I like the new syntax though. "before(:all)" and "before(:each)" seem better than "context_setup" and "setup". And by declaring all of them, it feels consistent and somewhat intuitive, as well as very customizable. I like it. Slightly tangential, but I hope not to much so... I am curious though if this could be made to work with my earlier suggestion: http://rubyforge.org/pipermail/rspec-users/2007-April/001180.html Specifically, a way of running a specific action ("example"?) for each expectation, but sometimes run it before and sometimes after (depending upon whether the expectation is a mock setup or a state based check). The way I wrote it before, I presented two new scenarios... but one of those scenarios could also be the default. describe Thing do before(:all) do #happens first, but only once within this "describe" block end before(:each) do #happens before each example within this "describe" block end for_each do # happens before each "it" example within this "describe" block end it "should ..." do #this is an expectation about the state after the "for_each" block end after(:each) do #happens after each example within this "describe" block end after(:all) do #happens last, but only once within this "describe" block end end or describe Thing do before(:all) do #happens first, but only once within this "describe" block end before(:each) do #happens before each example within this "describe" block end expect_that_it "should ..." do #this is the mock expectation end for_each do # happens after each "expect_that_it" example within this "describe" block end after(:each) do #happens after each example within this "describe" block end after(:all) do #happens last, but only once within this "describe" block end end Basically, the "for_each" block runs before each "it ''should...''" expectation, and after each "expect_that_it ''should...''" mock expectation. I think that this could significantly increase the readability of specs that use a lot of mock expectations, as well as DRY up the style proposed here: http://blog.davidchelimsky.net/articles/2006/11/09/tutorial-rspec-stubs-and-mocks But it might also make things more confusing... and I''m really not sure that "for_each" is the right phrase to describe the block (I''ve been using "action" up until now, and that doesn''t feel quite right either). I''m I chasing a bad approach here? -- Nick