Ashley Moran
2007-Feb-28 17:26 UTC
[rspec-users] Specifying that code is called in a block
Not sure if this is possible currently. I have a section of code like this: ActiveRecord::Base.transaction do cow.save! duck.save! dog.save! end (Names changed to protect the innocent.) I''d like to specify that the saves run in a transaction. I can do ActiveRecord::Base.should_receive(:transaction).and_yield But is there any way to specify that the code is called as above, and not as cow.save! ActiveRecord::Base.transaction do duck.save! end dog.save! ? Ta Ashley
aslak hellesoy
2007-Feb-28 21:27 UTC
[rspec-users] Specifying that code is called in a block
On 2/28/07, Ashley Moran <work at ashleymoran.me.uk> wrote:> Not sure if this is possible currently. > > I have a section of code like this: > > ActiveRecord::Base.transaction do > cow.save! > duck.save! > dog.save! > end > > (Names changed to protect the innocent.) > > I''d like to specify that the saves run in a transaction. I can do > > ActiveRecord::Base.should_receive(:transaction).and_yield > > But is there any way to specify that the code is called as above, and > not as > > cow.save! > ActiveRecord::Base.transaction do > duck.save! > end > dog.save! >You can specify order of messages (method calls) on one object (http://rspec.rubyforge.org/documentation/mocks/mocks.html) but not between different objects. I can''t help but feeling that if you''re specifying behaviour at this level of detail you''re too specific. You''re testing implementation, not verifying behaviour. Aslak> ? > > Ta > Ashley > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Matthijs Langenberg
2007-Mar-01 07:44 UTC
[rspec-users] Specifying that code is called in a block
I''m actually interested in the other way around: when do you know the solution should be receiving a block in a method when writing a specification? Can anyone give me an example of that? On 2/28/07, aslak hellesoy <aslak.hellesoy at gmail.com> wrote:> On 2/28/07, Ashley Moran <work at ashleymoran.me.uk> wrote: > > Not sure if this is possible currently. > > > > I have a section of code like this: > > > > ActiveRecord::Base.transaction do > > cow.save! > > duck.save! > > dog.save! > > end > > > > (Names changed to protect the innocent.) > > > > I''d like to specify that the saves run in a transaction. I can do > > > > ActiveRecord::Base.should_receive(:transaction).and_yield > > > > But is there any way to specify that the code is called as above, and > > not as > > > > cow.save! > > ActiveRecord::Base.transaction do > > duck.save! > > end > > dog.save! > > > > You can specify order of messages (method calls) on one object > (http://rspec.rubyforge.org/documentation/mocks/mocks.html) but not > between different objects. > > I can''t help but feeling that if you''re specifying behaviour at this > level of detail you''re too specific. You''re testing implementation, > not verifying behaviour. > > Aslak > > > ? > > > > Ta > > Ashley > > _______________________________________________ > > 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 >
Ashley Moran
2007-Mar-01 10:01 UTC
[rspec-users] Specifying that code is called in a block
On 28 Feb 2007, at 21:27, aslak hellesoy wrote:> You can specify order of messages (method calls) on one object > (http://rspec.rubyforge.org/documentation/mocks/mocks.html) but not > between different objects. > > I can''t help but feeling that if you''re specifying behaviour at this > level of detail you''re too specific. You''re testing implementation, > not verifying behaviour. > > AslakAslak, I thought about that, but then how can you test that things are being done in the correct transactions? (Which is really important, from where I stand!) The only way would be to involve the database in the tests, raise an exception during the code found in the block and test that the database hasn''t been modified. This seems like a step backwards, seeing as everything should be as isolated as possible. Or is the problem that ActiveRecord doesn''t have distinct business logic and persistence layers? Ashley
Ashley Moran
2007-Mar-01 10:35 UTC
[rspec-users] Specifying that code is called in a block
On 1 Mar 2007, at 07:44, Matthijs Langenberg wrote:> I''m actually interested in the other way around: when do you know the > solution should be receiving a block in a method when writing a > specification? Can anyone give me an example of that?Best I can think of is this: class MyClass def moo(quack_block) quack(&quack_block) end private def quack puts yield end end context "description of context" do setup do @mock = mock("mock") @my_object = MyClass.new end specify "descriptive specification" do @mock.should_receive(:woof) @my_object.moo(lambda { @mock.woof; "miaow" }) end end Not sure in the real world it would be practical, or even if this is a good thing to do, but it seems to work Ashley
Big thank-you for allowing rSpec core to be installed as a plugin. I''ve been on a merry-go-round of updating some 5 active projects to keep up with trunk. Whew! Thanks again. Steve
Bryan Helmkamp
2007-Mar-02 16:04 UTC
[rspec-users] Specifying that code is called in a block
On 2/28/07, aslak hellesoy <aslak.hellesoy at gmail.com> wrote:> I can''t help but feeling that if you''re specifying behaviour at this > level of detail you''re too specific. You''re testing implementation, > not verifying behaviour.Aslak, Is there an alternate solution you''d propose, or would you go without this spec entirely? It''s easy to imagine a case where the lack of specifying the need for a transaction could allow a bug. More generally, what metrics do you apply to differentiate between testing implementation and verifying behavior? As I understand it, if one were to follow a strict spec-first style of development, each piece of implementation would have to demanded by a failing spec before it could be written. In this particular case, that would mean not adding the transaction block until there is a unit spec requiring it. How do you reconcile these two seemingly conflicting principles? -Bryan
David Chelimsky
2007-Mar-02 17:01 UTC
[rspec-users] Specifying that code is called in a block
On 3/2/07, Bryan Helmkamp <bhelmkamp at gmail.com> wrote:> On 2/28/07, aslak hellesoy <aslak.hellesoy at gmail.com> wrote: > > I can''t help but feeling that if you''re specifying behaviour at this > > level of detail you''re too specific. You''re testing implementation, > > not verifying behaviour. > > Aslak, > > Is there an alternate solution you''d propose, or would you go without > this spec entirely? It''s easy to imagine a case where the lack of > specifying the need for a transaction could allow a bug.If it''s easy to imagine the case, then it _should_ (in theory ;) ) be easy to write a spec that exposes that bug.> More generally, what metrics do you apply to differentiate between > testing implementation and verifying behavior? > > As I understand it, if one were to follow a strict spec-first style of > development, each piece of implementation would have to demanded by a > failing spec before it could be written. In this particular case, that > would mean not adding the transaction block until there is a unit spec > requiring it. How do you reconcile these two seemingly conflicting > principles?<soapbox> TDD is not a perfect world, and BDD certainly does not make it so. There are cases in which we know things are important and we do some ugly things to test them. Bob Martin puts something similar into perspective talking about comments. He says that once in a while he''ll put a comment somewhere because it is more pragmatic to have the comment than any alternative, but that he views that as a failure. He doesn''t let it stop the world. He simply acknowledges that something is wrong and presses on. Similarly, in this case, absent a viable solution using the available tools, I''d probably do some nasty stuff to spec this out, but catalog that in the back of my mind as a failure of some combination of my skill and the available toolset. Tools like rspec are there to help you, and often do. But when they don''t, you can''t let that be an excuse for not producing software that is solid, maintainable, and meets the requirements at hand. </soapbox> David> > -Bryan > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Lugovoi Nikolai
2007-Mar-02 17:41 UTC
[rspec-users] Specifying that code is called in a block
What about such specification? specify "cow, dog and duck must be saved in the same transaction" do $log.clear @connection.stub!(:begin_db_transaction).and_return { $log.record("BEGIN"); nil } @connection.stub!(:commit_db_transaction).and_return { $log.record("COMMIT"); nil } @cow.stub!(:save!).and_return {$log.record("COW SAVE"); true} @dog.stub!(:save!).and_return {$log.record("DOG SAVE"); true} @duck.stub!(:save!).and_return {$log.record("DUCK SAVE"); true} @tested_object.exec_method_about_cow_dog_and_duck txn_intervals = %w(COW DUCK DOG).collect do |beast| [ $log.find("BEGIN", :before, "#{beast} SAVE"), $log.find("COMMIT", :after, "#{beast} SAVE")] end txn_intervals.each do |range| range[0].should_be kind_of(Time) range[1].should_be kind_of(Time) end txn_intervals.uniq.size.should == 1 end where $log can be something like: $log = Struct.new(:events, :timestamps).new def $log.clear events = [] timestamps = [] end def $log.record(what) events << what timestamps << Time.now end def $log.find(what, where, start) # return timestamp of event `what'' that is :before or :after (where) event `start'' end 2007/2/28, Ashley Moran <work at ashleymoran.me.uk>:> Not sure if this is possible currently. > > I have a section of code like this: > > ActiveRecord::Base.transaction do > cow.save! > duck.save! > dog.save! > end > > (Names changed to protect the innocent.) > > I''d like to specify that the saves run in a transaction. I can do > > ActiveRecord::Base.should_receive(:transaction).and_yield > > But is there any way to specify that the code is called as above, and > not as > > cow.save! > ActiveRecord::Base.transaction do > duck.save! > end > dog.save! > > ? > > Ta > Ashley > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Ashley Moran
2007-Mar-06 14:13 UTC
[rspec-users] Specifying that code is called in a block
Hey Lugovoi Not tried that yet but I like the look of it. You''d have to stub out ActiveRecord::Base.transaction to make it work (meddling with another API call!!! it''s starting to grate on me how often this is necessary) though which will surely break transactional fixtures? It just occured to me that I haven''t used transactional fixtures - I got confused why some specs were failing, although a recent post made me suspect my test database was just full of crap, and wrote a method to SQL delete the contents table by table. So I never noticed the fallout from stubbing ActiveRecord::Base.transaction, if there was any. I get the feeling this is something that could be generalised though. I''m just reading the paper Mock Roles, not Objects by four of the ThoughtWorks crew. Apparently jMock supports ordering of methods across mocks. I don''t know if this is something that''s been considered for RSpec. Thanks Ashley On 2 Mar 2007, at 17:41, Lugovoi Nikolai wrote:> What about such specification? > specify "cow, dog and duck must be saved in the same transaction" do > $log.clear > @connection.stub!(:begin_db_transaction).and_return { > $log.record("BEGIN"); nil } > @connection.stub!(:commit_db_transaction).and_return { > $log.record("COMMIT"); nil } > @cow.stub!(:save!).and_return {$log.record("COW SAVE"); true} > @dog.stub!(:save!).and_return {$log.record("DOG SAVE"); true} > @duck.stub!(:save!).and_return {$log.record("DUCK SAVE"); true} > > @tested_object.exec_method_about_cow_dog_and_duck > > txn_intervals = %w(COW DUCK DOG).collect do |beast| > [ $log.find("BEGIN", :before, "#{beast} SAVE"), > $log.find("COMMIT", :after, "#{beast} SAVE")] > end > > txn_intervals.each do |range| > range[0].should_be kind_of(Time) > range[1].should_be kind_of(Time) > end > txn_intervals.uniq.size.should == 1 > end > > where $log can be something like: > $log = Struct.new(:events, :timestamps).new > def $log.clear > events = [] > timestamps = [] > end > def $log.record(what) > events << what > timestamps << Time.now > end > def $log.find(what, where, start) > # return timestamp of event `what'' that is :before or :after > (where) event `start'' > end-- Codeweavers Ltd (Registered in England and Wales No. 04092394. VAT registration no. 823826816) Address: Unit 11, Imex Technology Park, ST4 8LJ. Tel: 0870 443 0888
Ashley Moran
2007-Mar-07 08:02 UTC
[rspec-users] Specifying that code is called in a block
Hey Lugovoi Not tried that yet but I like the look of it. You''d have to stub out ActiveRecord::Base.transaction to make it work (meddling with another API call!!! it''s starting to grate on me how often this is necessary) though which will surely break transactional fixtures? It just occured to me that I haven''t used transactional fixtures - I got confused why some specs were failing, although a recent post made me suspect my test database was just full of crap, and wrote a method to SQL delete the contents table by table. So I never noticed the fallout from stubbing ActiveRecord::Base.transaction, if there was any. I get the feeling this is something that could be generalised though. I''m just reading the paper Mock Roles, not Objects by four of the ThoughtWorks crew. Apparently jMock supports ordering of methods across mocks. I don''t know if this is something that''s been considered for RSpec. Thanks Ashley On 2 Mar 2007, at 17:41, Lugovoi Nikolai wrote:> What about such specification? > specify "cow, dog and duck must be saved in the same transaction" do > $log.clear > @connection.stub!(:begin_db_transaction).and_return { > $log.record("BEGIN"); nil } > @connection.stub!(:commit_db_transaction).and_return { > $log.record("COMMIT"); nil } > @cow.stub!(:save!).and_return {$log.record("COW SAVE"); true} > @dog.stub!(:save!).and_return {$log.record("DOG SAVE"); true} > @duck.stub!(:save!).and_return {$log.record("DUCK SAVE"); true} > > @tested_object.exec_method_about_cow_dog_and_duck > > txn_intervals = %w(COW DUCK DOG).collect do |beast| > [ $log.find("BEGIN", :before, "#{beast} SAVE"), > $log.find("COMMIT", :after, "#{beast} SAVE")] > end > > txn_intervals.each do |range| > range[0].should_be kind_of(Time) > range[1].should_be kind_of(Time) > end > txn_intervals.uniq.size.should == 1 > end > > where $log can be something like: > $log = Struct.new(:events, :timestamps).new > def $log.clear > events = [] > timestamps = [] > end > def $log.record(what) > events << what > timestamps << Time.now > end > def $log.find(what, where, start) > # return timestamp of event `what'' that is :before or :after > (where) event `start'' > end-- Codeweavers Ltd (Registered in England and Wales No. 04092394. VAT registration no. 823826816) Address: Unit 11, Imex Technology Park, ST4 8LJ. Tel: 0870 443 0888
aslak hellesoy
2007-Mar-07 16:45 UTC
[rspec-users] Specifying that code is called in a block
How is JMock supporting ordering of calls across mocks? I have been using JMock for years and have never seen that feature. On 3/7/07, Ashley Moran <work at ashleymoran.me.uk> wrote:> Hey Lugovoi > > Not tried that yet but I like the look of it. You''d have to stub out > ActiveRecord::Base.transaction to make it work (meddling with another > API call!!! it''s starting to grate on me how often this is necessary) > though which will surely break transactional fixtures? It just > occured to me that I haven''t used transactional fixtures - I got > confused why some specs were failing, although a recent post made me > suspect my test database was just full of crap, and wrote a method to > SQL delete the contents table by table. So I never noticed the > fallout from stubbing ActiveRecord::Base.transaction, if there was any. > > I get the feeling this is something that could be generalised > though. I''m just reading the paper Mock Roles, not Objects by four > of the ThoughtWorks crew. Apparently jMock supports ordering of > methods across mocks. I don''t know if this is something that''s been > considered for RSpec. > > Thanks > Ashley > > > > On 2 Mar 2007, at 17:41, Lugovoi Nikolai wrote: > > > What about such specification? > > specify "cow, dog and duck must be saved in the same transaction" do > > $log.clear > > @connection.stub!(:begin_db_transaction).and_return { > > $log.record("BEGIN"); nil } > > @connection.stub!(:commit_db_transaction).and_return { > > $log.record("COMMIT"); nil } > > @cow.stub!(:save!).and_return {$log.record("COW SAVE"); true} > > @dog.stub!(:save!).and_return {$log.record("DOG SAVE"); true} > > @duck.stub!(:save!).and_return {$log.record("DUCK SAVE"); true} > > > > @tested_object.exec_method_about_cow_dog_and_duck > > > > txn_intervals = %w(COW DUCK DOG).collect do |beast| > > [ $log.find("BEGIN", :before, "#{beast} SAVE"), > > $log.find("COMMIT", :after, "#{beast} SAVE")] > > end > > > > txn_intervals.each do |range| > > range[0].should_be kind_of(Time) > > range[1].should_be kind_of(Time) > > end > > txn_intervals.uniq.size.should == 1 > > end > > > > where $log can be something like: > > $log = Struct.new(:events, :timestamps).new > > def $log.clear > > events = [] > > timestamps = [] > > end > > def $log.record(what) > > events << what > > timestamps << Time.now > > end > > def $log.find(what, where, start) > > # return timestamp of event `what'' that is :before or :after > > (where) event `start'' > > end > > > > -- > > Codeweavers Ltd (Registered in England and Wales No. 04092394. VAT > registration no. 823826816) > Address: Unit 11, Imex Technology Park, ST4 8LJ. Tel: 0870 443 0888 > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Ashley Moran
2007-Mar-08 11:17 UTC
[rspec-users] Specifying that code is called in a block
On 7 Mar 2007, at 16:45, aslak hellesoy wrote:> How is JMock supporting ordering of calls across mocks? I have been > using JMock for years and have never seen that feature.From <http://www.jmock.org/oopsla2004.pdf>, section 3.4 public void testReturnsCachedObjectWithinTimeout() { mockLoader.expect(once()) .method("load").with( eq(KEY) ) .will( returnValue(VALUE) ); mockClock.expect(atLeastOnce()) .after(mockLoader, "load") .method("getCurrentTime").withNoArguments() .will( returnValues(loadTime, fetchTime) ); mockReloadPolicy.expect(atLeastOnce()) .method("shouldReload") .with( eq(loadTime), eq(fetchTime) ) .will( returnValue(false) ); assertSame( "should be loaded object", VALUE, cache.lookup(KEY) ); assertSame( "should be cached object", VALUE, cache.lookup(KEY) ); }