Sorry, lots of questions these days. Is there a normal approach for preconditions? In JUnit, I might put a few assertions in the setUp() just to make sure that the test ''data'' I''ve created meets expectations before going to test it. So, for instance, I''ve got an object that is audited using acts_as_audited and I''d like to test the XML that results from model.revisions.to_xml(). I''ve created the object and modified it twice, so I expect there to be three revisions, as far as the test goes. It''s not really a ''spec'', because it''s not part of the contract that model objects have three revisions, it''s just something I want to make sure is true before I run off and conduct some tests. I could raise an exception in the before block if the precondition isn''t met, any other choices? - Geoffrey -- Geoffrey Wiseman -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070907/855c462c/attachment-0001.html
On 9/7/07, Geoffrey Wiseman <geoffrey.wiseman at gmail.com> wrote:> Sorry, lots of questions these days. > > Is there a normal approach for preconditions? In JUnit, I might put a few > assertions in the setUp() just to make sure that the test ''data'' I''ve > created meets expectations before going to test it. > > So, for instance, I''ve got an object that is audited using acts_as_audited > and I''d like to test the XML that results from model.revisions.to_xml(). > I''ve created the object and modified it twice, so I expect there to be three > revisions, as far as the test goes. It''s not really a ''spec'', because it''s > not part of the contract that model objects have three revisions, it''s just > something I want to make sure is true before I run off and conduct some > tests. > > I could raise an exception in the before block if the precondition isn''t > met, any other choices? > > - Geoffrey > -- > Geoffrey Wiseman > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >I''m not sure why you say this isn''t a spec. Specs are about behavior of the system, not contracts. In this case you have two examples (imo). describe MyModel, " when saved twice" do before(:each) do @model = MyModel.create :title => "foo" 2.times { @model.save } end it "should have three revisions" do @model.should have(3).revisions end it "should render all revisions in a single XML doc" do Hash.from_xml(@model.to_xml).should = { "model" => { "revision" => 1, "title" => "foo" }, "model" => { "revision" => 2, "title" => "foo" }, "model" => { "revision" => 3, "title" => "foo" } } end end Also I would just have one description (like above) that verifies that it works as you expect. In specs for other behaviors you can then just assume that it works. Pat
On 9/7/07, Pat Maddox <pergesu at gmail.com> wrote:> > describe MyModel, " when saved twice" do >This is the key point I hadn''t considered; makes sense, as long as you make sure that there''s a context wherein the specification that it should have a certain number of revisions. Thanks! Realized this afternoon that I need to use finer-grained ''test methods'' than I''m used to. In some ways the it blocks are closer to assertions than to test methods in Test::Unit, in some ways. So I went from my first crack, closer to: describe Customer, "xml" do before do # set up customer end it "should generate valid summary xml" do # generate summary xml # a bunch of shoulds about the xml end it "should generate valid full xml" do # generate full xml # a bunch of shoulds end end To something like this: describe Customer, "full xml" do before do # set up customer full xml end it "should have a root node of customer" do @doc.root.name.should == ''customer'' end it "should contain a customer id" it "should have a name and address" #etc end describe Customer, "summary xml" do # etc. end So I guess I''m still learning the mindset in places. Although you could ahve test methods like that in Test::Unit, most of the time you wouldn''t bother simply because of the shared setup. But when the blocks affect how the spec is described, it''s far more important to have fine-grained elements, I think. Actually, on that note -- what''s the normal way to share a setup across multiple behaviors, a helper method in an includeable module? So, thanks for the guidance. -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070907/1f0d83ce/attachment.html
On 9/7/07, Geoffrey Wiseman <geoffrey.wiseman at gmail.com> wrote:> On 9/7/07, Pat Maddox <pergesu at gmail.com> wrote: > > describe MyModel, " when saved twice" do > > > > This is the key point I hadn''t considered; makes sense, as long as you make > sure that there''s a context wherein the specification that it should have a > certain number of revisions. Thanks! > > Realized this afternoon that I need to use finer-grained ''test methods'' than > I''m used to. In some ways the it blocks are closer to assertions than to > test methods in Test::Unit, in some ways. So I went from my first crack, > closer to: > > describe Customer, "xml" do > before do > # set up customer > end > > it "should generate valid summary xml" do > # generate summary xml > # a bunch of shoulds about the xml > end > > it "should generate valid full xml" do > # generate full xml > # a bunch of shoulds > end > end > > To something like this: > describe Customer, "full xml" do > before do > # set up customer full xml > end > > it "should have a root node of customer" do > @doc.root.name.should == ''customer'' > end > > it "should contain a customer id" > it "should have a name and address" > > #etc > end > > describe Customer, "summary xml" do > # etc. > end > > So I guess I''m still learning the mindset in places. Although you could > ahve test methods like that in Test::Unit, most of the time you wouldn''t > bother simply because of the shared setup. But when the blocks affect how > the spec is described, it''s far more important to have fine-grained > elements, I think. > > Actually, on that note -- what''s the normal way to share a setup across > multiple behaviors, a helper method in an includeable module?You can include callable methods in modules, but they have to be called from within the spec. Alternative is shared behaviours: http://rspec.rubyforge.org/documentation/index.html (scroll down a bit).> > So, thanks for the guidance. > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
On 9/7/07, Geoffrey Wiseman <geoffrey.wiseman at gmail.com> wrote:> On 9/7/07, Pat Maddox <pergesu at gmail.com> wrote: > > describe MyModel, " when saved twice" do > > > > This is the key point I hadn''t considered; makes sense, as long as you make > sure that there''s a context wherein the specification that it should have a > certain number of revisions. Thanks! > > Realized this afternoon that I need to use finer-grained ''test methods'' than > I''m used to. In some ways the it blocks are closer to assertions than to > test methods in Test::Unit, in some ways.Totally. I highly recommend Dave Astels'' "One Expectation per Example" article [1]. A couple separate points: * RSpec kicks ass because you can specify behavior on whatever level you want. You can use Story Runner to specify a feature, exercising a vertical slice of your entire app. You can get really fine-grained and specify what happens when a method runs. * Descriptions should be broken up based on the required fixture. I don''t split them up until I actually have to. For example, if I''m writing a Stack class. I''d probably start off with describe Stack do it "should be empty" do Stack.new.should be_empty end end Then I want to write a spec for when I''ve added an item. describe Stack do it "should be empty" do Stack.new.should be_empty end it "should not be empty after adding an item" do s = Stack.new s.add_item :foo s.should_not be_empty end end For a simple spec like this it''s okay. We could factor out the Stack.new call, and there''s one other smell, but we''ll get to that in a minute. Now what if we want to peek the stack? describe Stack do it "should be empty" do Stack.new.should be_empty end it "should not be empty after adding an item" do s = Stack.new s.add_item :foo s.should_not be_empty end it "should let you peek at the top after adding an item" do s = Stack.new s.add_item s.peek.should == :foo end end Now we''ve got clear duplication in three places: (1) The constructor (2) Call to add_item (3) the ''it'' specifier! It''s clear that the fixture for "should not be empty" and "should let you peek" are the same. They''re also different from the "should be empty" so we split them up: describe Stack, " empty" do it "should be empty" do Stack.new.should be_empty end end describe Stack, " with one item" do before(:each) do @stack = Stack.new @stack.add_item :foo end it "should not be empty" do @stack.should_not be_empty end it "should let you peek at the top" do @stack.peek.should == :foo end end There are two key benefits to that. The first is that it''s obvious where new specifications need to go. The behavior for #pop whether a stack is empty or has an item is going to be different. Also if you need some behavior that changes with 3 items, you can probably figure out that you should create a new description. An even bigger benefit is that it minimizes the brain processing required to figure out a spec. If you create the fixture in the setup and don''t vary it, it''s trivial to scan through some simple expectations. This has to do with the smell that I alluded to earlier, which was the call to add_item. Ideally your example will contain just one expectation and no other setup. This reduces the concepts that change from example to example. Change is where bugs pop up most of the time. So if you''re doing setup in an example, then you probably want to split it out from the current description. In fact, in real life I would have split the descriptions up immediately after writing the "should not be empty" example.> So I went from my first crack, > closer to: > > describe Customer, "xml" do > before do > # set up customer > end > > it "should generate valid summary xml" do > # generate summary xml > # a bunch of shoulds about the xml > end > > it "should generate valid full xml" do > # generate full xml > # a bunch of shoulds > end > end > > To something like this: > describe Customer, "full xml" do > before do > # set up customer full xml > end > > it "should have a root node of customer" do > @doc.root.name.should == ''customer'' > end > > it "should contain a customer id" > it "should have a name and address" > > #etc > end > > describe Customer, "summary xml" do > # etc. > end > > So I guess I''m still learning the mindset in places. Although you could > ahve test methods like that in Test::Unit, most of the time you wouldn''t > bother simply because of the shared setup. But when the blocks affect how > the spec is described, it''s far more important to have fine-grained > elements, I think.There''s nothing in Test::Unit that prohibits you from writing tests with the same granularity that you can achieve with RSpec. However I have noticed that it can feel unnatural to do so, whereas RSpec of course highly encourages it.> Actually, on that note -- what''s the normal way to share a setup across > multiple behaviors, a helper method in an includeable module?There are a couple ways to do this. The first is writing helper methods, as you said. For example I use this all the time describe ProductsController, " requesting /products/1 using GET as a logged in user" do include UserSpecHelpers before(:each) do login_as mock_user @products_proxy = stub("products proxy", :find => :foo) mock_user.stub!(:products).and_return @products_proxy end def do_get get :show, :id => "1" end it "should find the product" do @products_proxy.should_receive(:find).with("1").and_return :foo do_get end it "should assign the product to the view" do do_get assigns[:product].should == :foo end end module UserSpecHelpers def mock_user @mock_user ||= mock_model(User, :admin? => false) end end You can see that we stubbed mock_user#products there. You can also set expectations if you want. Another way is to use a factory. Check out http://www.dcmanges.com/blog/38 for a description of that technique. Though I''d prefer to use mock objects instead of AR instances of course! Both of those are pretty fine-grained approaches, allowing you to share creation of helper objects. What if there''s still duplication, for example you''re typing the same 8 lines of creation code in? Or if lots of your examples are the same? That''s where shared behaviors come in. Check out the docs [2] for more on how to use them. They let you share setup/teardown code AND examples. In the past if you wrote three different specs for "/products/1" that had some of the same examples, you''d probably just remove the common examples from two of the specs, knowing that the first spec already tested it. With shared specs you can actually factor out that behavior but have it be included into all the specs so they specify the behavior without the hassle and errors that come with duplication. Pat [1] http://daveastels.com/2006/08/26/one-expectation-per-example-a-remake-of-one-assertion-per-test/ [2] http://rspec.rubyforge.org/documentation/index.html
On 9/7/07, David Chelimsky <dchelimsky at gmail.com> wrote:> > > Actually, on that note -- what''s the normal way to share a setup across > > multiple behaviors, a helper method in an includeable module? > > You can include callable methods in modules, but they have to be > called from within the spec. > > Alternative is shared behaviours: > http://rspec.rubyforge.org/documentation/index.html (scroll down a > bit).I''ve already got some shared behaviors, they''re great; nice way to use a unit-level assertions n higher-level tests for instance without breaking DRY. Hadn''t really thought about using these just for setup; the syntax would look a little weird what with it_should_behave_like, but it''s still a thought. -- Geoffrey Wiseman -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070907/ed50da40/attachment.html
On 9/7/07, Geoffrey Wiseman <geoffrey.wiseman at gmail.com> wrote:> On 9/7/07, David Chelimsky <dchelimsky at gmail.com> wrote: > > > Actually, on that note -- what''s the normal way to share a setup across > > > multiple behaviors, a helper method in an includeable module? > > > > You can include callable methods in modules, but they have to be > > called from within the spec. > > > > Alternative is shared behaviours: > > http://rspec.rubyforge.org/documentation/index.html > (scroll down a > > bit). > > > I''ve already got some shared behaviors, they''re great; nice way to use a > unit-level assertions n higher-level tests for instance without breaking > DRY. > > Hadn''t really thought about using these just for setup; the syntax would > look a little weird what with it_should_behave_like, but it''s still a > thought.Sorry - you''re right - not the place for setup. So best to stick w/ included modules.> > -- > Geoffrey Wiseman > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
El 8/9/2007, a las 2:15, Pat Maddox escribi?:> * Descriptions should be broken up based on the required fixture. I > don''t split them up until I actually have to. For example, if I''m > writing a Stack class. I''d probably start off with > > [snip] > > For a simple spec like this it''s okay. We could factor out the > Stack.new call, and there''s one other smell, but we''ll get to that in > a minute. > > Now what if we want to peek the stack? > > [snip] > > Now we''ve got clear duplication in three places: > (1) The constructor > (2) Call to add_item > (3) the ''it'' specifier! > > It''s clear that the fixture for "should not be empty" and "should let > you peek" are the same. They''re also different from the "should be > empty" so we split them up: > > [snip] > > There are two key benefits to that. The first is that it''s obvious > where new specifications need to go. The behavior for #pop whether a > stack is empty or has an item is going to be different. Also if you > need some behavior that changes with 3 items, you can probably figure > out that you should create a new description. > > An even bigger benefit is that it minimizes the brain processing > required to figure out a spec. If you create the fixture in the setup > and don''t vary it, it''s trivial to scan through some simple > expectations. > > This has to do with the smell that I alluded to earlier, which was the > call to add_item. Ideally your example will contain just one > expectation and no other setup. This reduces the concepts that change > from example to example. Change is where bugs pop up most of the > time. So if you''re doing setup in an example, then you probably want > to split it out from the current description. In fact, in real life I > would have split the descriptions up immediately after writing the > "should not be empty" example.Brilliantly written example, very clear! +1 Cheers, Wincent
On 9/8/07, Wincent Colaiuta <win at wincent.com> wrote:> El 8/9/2007, a las 2:15, Pat Maddox escribi?: > > > * Descriptions should be broken up based on the required fixture. I > > don''t split them up until I actually have to. For example, if I''m > > writing a Stack class. I''d probably start off with > > > > [snip] > > > > For a simple spec like this it''s okay. We could factor out the > > Stack.new call, and there''s one other smell, but we''ll get to that in > > a minute. > > > > Now what if we want to peek the stack? > > > > [snip] > > > > Now we''ve got clear duplication in three places: > > (1) The constructor > > (2) Call to add_item > > (3) the ''it'' specifier! > > > > It''s clear that the fixture for "should not be empty" and "should let > > you peek" are the same. They''re also different from the "should be > > empty" so we split them up: > > > > [snip] > > > > There are two key benefits to that. The first is that it''s obvious > > where new specifications need to go. The behavior for #pop whether a > > stack is empty or has an item is going to be different. Also if you > > need some behavior that changes with 3 items, you can probably figure > > out that you should create a new description. > > > > An even bigger benefit is that it minimizes the brain processing > > required to figure out a spec. If you create the fixture in the setup > > and don''t vary it, it''s trivial to scan through some simple > > expectations. > > > > This has to do with the smell that I alluded to earlier, which was the > > call to add_item. Ideally your example will contain just one > > expectation and no other setup. This reduces the concepts that change > > from example to example. Change is where bugs pop up most of the > > time. So if you''re doing setup in an example, then you probably want > > to split it out from the current description. In fact, in real life I > > would have split the descriptions up immediately after writing the > > "should not be empty" example. > > Brilliantly written example, very clear! > > +1 > > Cheers, > Wincent > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >To give credit where it''s due, I''m pretty sure that''s from my memory of the early examples on rspec.rubyforge.org. Pat
El 8/9/2007, a las 17:18, Pat Maddox escribi?:> To give credit where it''s due, I''m pretty sure that''s from my memory > of the early examples on rspec.rubyforge.org.Yes, but it''s one thing to look at a finished set of well-written specs, and it''s another to see them built up from scratch in steps and have a clear explanation of the logic behind your decisions. I think you did a great job. Cheers, Wincent
On 9/8/07, Pat Maddox <pergesu at gmail.com> wrote:> On 9/8/07, Wincent Colaiuta <win at wincent.com> wrote: > > El 8/9/2007, a las 2:15, Pat Maddox escribi?: > > > > > * Descriptions should be broken up based on the required fixture. I > > > don''t split them up until I actually have to. For example, if I''m > > > writing a Stack class. I''d probably start off with > > > > > > [snip] > > > > > > For a simple spec like this it''s okay. We could factor out the > > > Stack.new call, and there''s one other smell, but we''ll get to that in > > > a minute. > > > > > > Now what if we want to peek the stack? > > > > > > [snip] > > > > > > Now we''ve got clear duplication in three places: > > > (1) The constructor > > > (2) Call to add_item > > > (3) the ''it'' specifier! > > > > > > It''s clear that the fixture for "should not be empty" and "should let > > > you peek" are the same. They''re also different from the "should be > > > empty" so we split them up: > > > > > > [snip] > > > > > > There are two key benefits to that. The first is that it''s obvious > > > where new specifications need to go. The behavior for #pop whether a > > > stack is empty or has an item is going to be different. Also if you > > > need some behavior that changes with 3 items, you can probably figure > > > out that you should create a new description. > > > > > > An even bigger benefit is that it minimizes the brain processing > > > required to figure out a spec. If you create the fixture in the setup > > > and don''t vary it, it''s trivial to scan through some simple > > > expectations. > > > > > > This has to do with the smell that I alluded to earlier, which was the > > > call to add_item. Ideally your example will contain just one > > > expectation and no other setup. This reduces the concepts that change > > > from example to example. Change is where bugs pop up most of the > > > time. So if you''re doing setup in an example, then you probably want > > > to split it out from the current description. In fact, in real life I > > > would have split the descriptions up immediately after writing the > > > "should not be empty" example. > > > > Brilliantly written example, very clear! > > > > +1 > > > > Cheers, > > Wincent > > > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > http://rubyforge.org/mailman/listinfo/rspec-users > > > > To give credit where it''s due, I''m pretty sure that''s from my memory > of the early examples on rspec.rubyforge.org.Thanks for that Pat. We pulled that tutorial down a while ago but you can get the textile source for it like so: svn export svn://rubyforge.org/var/svn/rspec/tags/REL_0_8_0/doc/src/tutorials Cheers, David> > Pat > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
David Chelimsky wrote:> On 9/8/07, Pat Maddox <pergesu at gmail.com> wrote: > >> On 9/8/07, Wincent Colaiuta <win at wincent.com> wrote: >> >>> El 8/9/2007, a las 2:15, Pat Maddox escribi?: >>> >>> >>>> * Descriptions should be broken up based on the required fixture. I >>>> don''t split them up until I actually have to. For example, if I''m >>>> writing a Stack class. I''d probably start off with >>>> >>>> [snip] >>>> >>>> For a simple spec like this it''s okay. We could factor out the >>>> Stack.new call, and there''s one other smell, but we''ll get to that in >>>> a minute. >>>> >>>> Now what if we want to peek the stack? >>>> >>>> [snip] >>>> >>>> Now we''ve got clear duplication in three places: >>>> (1) The constructor >>>> (2) Call to add_item >>>> (3) the ''it'' specifier! >>>> >>>> It''s clear that the fixture for "should not be empty" and "should let >>>> you peek" are the same. They''re also different from the "should be >>>> empty" so we split them up: >>>> >>>> [snip] >>>> >>>> There are two key benefits to that. The first is that it''s obvious >>>> where new specifications need to go. The behavior for #pop whether a >>>> stack is empty or has an item is going to be different. Also if you >>>> need some behavior that changes with 3 items, you can probably figure >>>> out that you should create a new description. >>>> >>>> An even bigger benefit is that it minimizes the brain processing >>>> required to figure out a spec. If you create the fixture in the setup >>>> and don''t vary it, it''s trivial to scan through some simple >>>> expectations. >>>> >>>> This has to do with the smell that I alluded to earlier, which was the >>>> call to add_item. Ideally your example will contain just one >>>> expectation and no other setup. This reduces the concepts that change >>>> from example to example. Change is where bugs pop up most of the >>>> time. So if you''re doing setup in an example, then you probably want >>>> to split it out from the current description. In fact, in real life I >>>> would have split the descriptions up immediately after writing the >>>> "should not be empty" example. >>>> >>> Brilliantly written example, very clear! >>> >>> +1 >>> >>> Cheers, >>> Wincent >>> >>> _______________________________________________ >>> rspec-users mailing list >>> rspec-users at rubyforge.org >>> http://rubyforge.org/mailman/listinfo/rspec-users >>> >>> >> To give credit where it''s due, I''m pretty sure that''s from my memory >> of the early examples on rspec.rubyforge.org. >> > > Thanks for that Pat. We pulled that tutorial down a while ago but you > can get the textile source for it like so: > > svn export svn://rubyforge.org/var/svn/rspec/tags/REL_0_8_0/doc/src/tutorials > > Cheers, > David > > >> Pat >> _______________________________________________ >> rspec-users mailing list >> rspec-users at rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users >> >> > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >What was the motivation behind taking that tutorial down, BTW? I really like and benefited from it when I was starting to learn rspec. Was it just a maintenance issue as far as updating the tutorial to the current API (DSL)? -Ben
On 9/8/07, Ben Mabey <ben at benmabey.com> wrote:> What was the motivation behind taking that tutorial down, BTW? I really > like and benefited from it when I was starting to learn rspec. Was it > just a maintenance issue as far as updating the tutorial to the current > API (DSL)?That was part of the deal. The other part was that I thought it would be more demonstrative to have something w/ two or more classes dealing w/ interactions. Just never got around to it. We should probably resurrect the Stack tutorial in the mean time.