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