Am I correct in understanding that the point of using let is that it can minimize database calls due to memoization? describe "#something" do let(:something) = Something.groovy_scope it "does x" do something.first.x.should have_coolness end it "does y" do something.first.y.should_not have_coolness end end vs. describe "#something" do before :each do @something = Something.groovy_scope end it "does x" do @something.first.x.should_not have_coolness end it "does y" do @something.first.y.should_not have_coolness end end ... Is that the proper usage? Patrick J. Collins http://collinatorstudios.com
> Am I correct in understanding that the point of using let is that it can > minimize database calls due to memoization? > > describe "#something" do > > let(:something) = Something.groovy_scopeand obviously I meant let(:something) { ... }, not (oops)... Patrick J. Collins http://collinatorstudios.com
On Sep 30, 2011, at 2:48 PM, Patrick J. Collins wrote:> Am I correct in understanding that the point of using let is that it can > minimize database calls due to memoization? > > describe "#something" do > > let(:something) = Something.groovy_scope > > it "does x" do > something.first.x.should have_coolness > end > > it "does y" do > something.first.y.should_not have_coolness > end > > end > > vs. > > describe "#something" do > > before :each do > @something = Something.groovy_scope > end > > it "does x" do > @something.first.x.should_not have_coolness > end > > it "does y" do > @something.first.y.should_not have_coolness > end > > end > > ... > > Is that the proper usage?Proper usage, sure, but the memoization is only within each example - not across examples. That way you can do this: let(:thing) { Thing.new } it "does something" do thing.blah thing.whatever thing.yet_again end In that case each reference to thing returns the same object. Make sense?
> Proper usage, sure, but the memoization is only within each example - not across examples. That way you can do this: > > let(:thing) { Thing.new } > > it "does something" do > thing.blah > thing.whatever > thing.yet_again > end > > In that case each reference to thing returns the same object. > > Make sense?Hmm.. now I am confused... What is the difference between: describe "Foo" do let(:foo) { Foo.new } it "is tubular" do foo.bar foo.baz end it "is gnarly" do foo.gnarl foo.wurd_up end end vs. describe "Foo" do before :each do @foo = Foo.new end it "seems just as tubular as the foo w/ let" do @foo.bar @foo.baz end it "seems just as gnarly as the foo w/ let" do foo.gnarl foo.wurd_up end end I am not seeing any difference...? Patrick J. Collins http://collinatorstudios.com
On Sep 30, 2011, at 3:31 PM, Patrick J. Collins wrote:>> Proper usage, sure, but the memoization is only within each example - not across examples. That way you can do this: >> >> let(:thing) { Thing.new } >> >> it "does something" do >> thing.blah >> thing.whatever >> thing.yet_again >> end >> >> In that case each reference to thing returns the same object. >> >> Make sense? > > Hmm.. now I am confused... > > What is the difference between: > > describe "Foo" do > > let(:foo) { Foo.new } > > it "is tubular" do > foo.bar > foo.baz > end > > it "is gnarly" do > foo.gnarl > foo.wurd_up > end > > end > > vs. > > describe "Foo" do > > before :each do > @foo = Foo.new > end > > it "seems just as tubular as the foo w/ let" do > @foo.bar > @foo.baz > end > > it "seems just as gnarly as the foo w/ let" do > @foo.gnarl > @foo.wurd_up > end > > end > > I am not seeing any difference...?There is not, really, other than how the declaration of foo is expressed and referenced. This evolved out of a common pattern in TDD: 1: describe "something" do it "does something" do thing = Thing.new thing.do_something.should have_some_outcome end end 2: describe "something" do it "does something" do thing = Thing.new thing.do_something.should have_some_outcome end it "does something else" do thing = Thing.new thing.do_something_else.should have_some_other_outcome end end Now there is duplication so we can refactor out the declaration of thing. It takes less work and is less error prone to change it to a let declaration than to change the references to thing to an instance variable declared in a before hook. Cheers, David
On 30/09/11 3:58 PM, David Chelimsky wrote:> On Sep 30, 2011, at 3:31 PM, Patrick J. Collins wrote: > >>> Proper usage, sure, but the memoization is only within each example - not across examples. That way you can do this: >>> >>> let(:thing) { Thing.new } >>> >>> it "does something" do >>> thing.blah >>> thing.whatever >>> thing.yet_again >>> end >>> >>> In that case each reference to thing returns the same object. >>> >>> Make sense? >> Hmm.. now I am confused... >> >> What is the difference between: >> >> describe "Foo" do >> >> let(:foo) { Foo.new } >> >> it "is tubular" do >> foo.bar >> foo.baz >> end >> >> it "is gnarly" do >> foo.gnarl >> foo.wurd_up >> end >> >> end >> >> vs. >> >> describe "Foo" do >> >> before :each do >> @foo = Foo.new >> end >> >> it "seems just as tubular as the foo w/ let" do >> @foo.bar >> @foo.baz >> end >> >> it "seems just as gnarly as the foo w/ let" do >> @foo.gnarl >> @foo.wurd_up >> end >> >> end >> >> I am not seeing any difference...? > There is not, really, other than how the declaration of foo is expressed and referenced. This evolved out of a common pattern in TDD: > > 1: > > describe "something" do > it "does something" do > thing = Thing.new > thing.do_something.should have_some_outcome > end > end > > 2: > > describe "something" do > it "does something" do > thing = Thing.new > thing.do_something.should have_some_outcome > end > > it "does something else" do > thing = Thing.new > thing.do_something_else.should have_some_other_outcome > end > end > > Now there is duplication so we can refactor out the declaration of thing. It takes less work and is less error prone to change it to a let declaration than to change the references to thing to an instance variable declared in a before hook. > > Cheers, > David > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >One thing I noticed, and something that always catches me out is the fact that the ''let''ted object doesn''t get instantiated until it gets referenced. Therefore, describe "all" do let(:foo) { Foo.create! } it "returns the created object" do Foo.all.should include(foo) end end ... fails, since at the time of calling :all, the ''foo'' object hasn''t been referred yet, and hence the block hasn''t executed. "Foo.all" in the case above returns an empty array, which wouldn''t have been the case with an instance object created in "before(:each)". Srushti http://c42.in
On Sep 30, 2011, at 4:32 PM, Srushti Ambekallu wrote:> On 30/09/11 3:58 PM, David Chelimsky wrote: >> On Sep 30, 2011, at 3:31 PM, Patrick J. Collins wrote: >> >>>> Proper usage, sure, but the memoization is only within each example - not across examples. That way you can do this: >>>> >>>> let(:thing) { Thing.new } >>>> >>>> it "does something" do >>>> thing.blah >>>> thing.whatever >>>> thing.yet_again >>>> end >>>> >>>> In that case each reference to thing returns the same object. >>>> >>>> Make sense? >>> Hmm.. now I am confused... >>> >>> What is the difference between: >>> >>> describe "Foo" do >>> >>> let(:foo) { Foo.new } >>> >>> it "is tubular" do >>> foo.bar >>> foo.baz >>> end >>> >>> it "is gnarly" do >>> foo.gnarl >>> foo.wurd_up >>> end >>> >>> end >>> >>> vs. >>> >>> describe "Foo" do >>> >>> before :each do >>> @foo = Foo.new >>> end >>> >>> it "seems just as tubular as the foo w/ let" do >>> @foo.bar >>> @foo.baz >>> end >>> >>> it "seems just as gnarly as the foo w/ let" do >>> @foo.gnarl >>> @foo.wurd_up >>> end >>> >>> end >>> >>> I am not seeing any difference...? >> There is not, really, other than how the declaration of foo is expressed and referenced. This evolved out of a common pattern in TDD: >> >> 1: >> >> describe "something" do >> it "does something" do >> thing = Thing.new >> thing.do_something.should have_some_outcome >> end >> end >> >> 2: >> >> describe "something" do >> it "does something" do >> thing = Thing.new >> thing.do_something.should have_some_outcome >> end >> >> it "does something else" do >> thing = Thing.new >> thing.do_something_else.should have_some_other_outcome >> end >> end >> >> Now there is duplication so we can refactor out the declaration of thing. It takes less work and is less error prone to change it to a let declaration than to change the references to thing to an instance variable declared in a before hook. >> >> Cheers, >> David >> >> _______________________________________________ >> rspec-users mailing list >> rspec-users at rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users >> > One thing I noticed, and something that always catches me out is the fact that the ''let''ted object doesn''t get instantiated until it gets referenced. Therefore, > > describe "all" do > let(:foo) { Foo.create! } > > it "returns the created object" do > Foo.all.should include(foo) > end > end > > ... fails, since at the time of calling :all, the ''foo'' object hasn''t been referred yet, and hence the block hasn''t executed. "Foo.all" in the case above returns an empty array, which wouldn''t have been the case with an instance object created in "before(:each)". > > Srushti > http://c42.inThat''s one of the benefits of let :) You can use it to declare objects but they only get instantiated when you reference them. See https://www.relishapp.com/rspec/rspec-core/docs/helper-methods/let-and-let
> Proper usage, sure, but the memoization is only within each example - not > across examples. That way you can do this:So regarding objects persisting over multiple examples-- I was told repeatedly by experienced RSpec peeps to not use before(:all)... But in a case like: before(:each) do @user = create_user create_user_item(:user => @user) end it "has an item" do @user.user_item.should_not be_nil end it "rocks the house" do @user.user_item.should respond_to(:rocks_the_house) end ..etc... It seems like this is an instance where before :all, really would shine because it would not require records to be repeatedly created... Do you guys feel like before(:all) is just bad because of the possibility of a method call in one example changing the state and therefore breaking future examples and not having it be clear as to why.. ? Patrick J. Collins http://collinatorstudios.com
On 30/09/11 6:24 PM, Patrick J. Collins wrote:>> Proper usage, sure, but the memoization is only within each example - not >> across examples. That way you can do this: > So regarding objects persisting over multiple examples-- I was told repeatedly > by experienced RSpec peeps to not use before(:all)... > > But in a case like: > > before(:each) do > @user = create_user > create_user_item(:user => @user) > end > > it "has an item" do > @user.user_item.should_not be_nil > end > > it "rocks the house" do > @user.user_item.should respond_to(:rocks_the_house) > end > > ..etc... > > It seems like this is an instance where before :all, really would shine because > it would not require records to be repeatedly created... > > Do you guys feel like before(:all) is just bad because of the possibility of a > method call in one example changing the state and therefore breaking future > examples and not having it be clear as to why.. ? > > > > Patrick J. Collins > http://collinatorstudios.com > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >I don''t know if object creation (including the corresponding database calls) are really expensive enough that I would worry about optimising that (unless you''re getting it anyway, without losing anything in the process, like with ''let''). Occasionally, for some reason I''ll end up using a ''before(:all)'' for one isolated scenario, but inevitably a few months down the line, I''ll end up wasting a couple of hours on a weird failing test, until I look closely enough to notice the ":all". So, unless I''m doing something that''s prohibitively expensive (I haven''t found anything that would qualify in any of my projects) I''m not likely to create anything in a ''before(:all)''. Srushti http://c42.in
On 1 Oct 2011, at 00:36, Srushti Ambekallu wrote:> On 30/09/11 6:24 PM, Patrick J. Collins wrote: >> So regarding objects persisting over multiple examples-- I was told repeatedly >> by experienced RSpec peeps to not use before(:all)? > > I don''t know if object creation (including the corresponding database calls) are really expensive enough that I would worry about optimising that (unless you''re getting it anyway, without losing anything in the process, like with ''let''). Occasionally, for some reason I''ll end up using a ''before(:all)'' for one isolated scenario, but inevitably a few months down the line, I''ll end up wasting a couple of hours on a weird failing test, until I look closely enough to notice the ":all". > > So, unless I''m doing something that''s prohibitively expensive (I haven''t found anything that would qualify in any of my projects) I''m not likely to create anything in a ''before(:all)''.I''ll second that. It''s better to feel the pain of the multiple database hits and refactor it out in the code (can you separate more of the logic and persistence?), than try to optimise the test run by introducing shared state. In the long run, the former will give you many faster test runs through better design, where as the latter will give you a few faster tests now at the risk of - as Srushti points out - stepping on a landmine later. HTH Ash -- http://www.patchspace.co.uk/ http://www.linkedin.com/in/ashmoran