nruth
2010-Jul-28 22:42 UTC
[rspec-users] rspec 1 - nested examples (in an each block) use the final block variable 4 times instead of each of the 4 once
Please correct me if this is fixed in Rspec 2, but in Rspec 1 I have hit upon the following problem (at least thrice, this time it costing me several hours), code is the best example: I have a practice examination system where each record is one of several "subtests" (think tagging rather than subclassing) which is part of a constant string array. I want to test each''s behaviour indepently in my specs (since an earlier implementation used STI rather than tagging, and I want to test each kind of test follows the spec) My specs use this approach in parts: describe "#add_question_set_of_type" do MyModule::SUBTESTS.each do |subtest| describe %Q("#{subtest}") do ? specs using subtest end end where SUBTESTS is a constant array of strings. the problem is doing this breaks any blocks that look like let(:subtest) {MyModule.subtest_to_sym(subtest)} or before(:each) do @subtest = MyModule.subtest_to_sym(subtest) end Instead I have to avoid the @ or let examples, and use the more explicit (verbose): MyModule.subtest_to_sym(subtest) using the let or before @ approaches both fail. Using puts within the code (for debugging purposes) I found that the specs were only being exposed to the final value of the array, while I expected (and have seen, or at least assumed from past passes) that all 4 (or so) of the strings were being used to create unique methods on the example group (one per iteration). Is this something that should be: a. avoided, because it''s crazy, and written differently b, documented c. investigated further I can provide a more complete example if helpful? Thanks, Nick
David Chelimsky
2010-Jul-29 00:22 UTC
[rspec-users] rspec 1 - nested examples (in an each block) use the final block variable 4 times instead of each of the 4 once
On Jul 28, 2010, at 5:42 PM, nruth wrote:> Please correct me if this is fixed in Rspec 2, but in Rspec 1 I have > hit upon the following problem (at least thrice, this time it costing > me several hours), code is the best example: > > I have a practice examination system where each record is one of > several "subtests" (think tagging rather than subclassing) which is > part of a constant string array. I want to test each''s behaviour > indepently in my specs (since an earlier implementation used STI > rather than tagging, and I want to test each kind of test follows the > spec) > > My specs use this approach in parts: > > describe "#add_question_set_of_type" do > MyModule::SUBTESTS.each do |subtest| > > describe %Q("#{subtest}") do > ? specs using subtest > end > end > > where SUBTESTS is a constant array of strings. > > the problem is doing this breaks any blocks that look likeWhere are these blocks, in the outer or inner example group? Please provide a complete example, including everything I need to run and see the output you''re seeing. Thx> > let(:subtest) {MyModule.subtest_to_sym(subtest)} > > or > > before(:each) do > @subtest = MyModule.subtest_to_sym(subtest) > end > > Instead I have to avoid the @ or let examples, and use the more > explicit (verbose): > > MyModule.subtest_to_sym(subtest) > > using the let or before @ approaches both fail. > > Using puts within the code (for debugging purposes) I found that the > specs were only being exposed to the final value of the array, while I > expected (and have seen, or at least assumed from past passes) that > all 4 (or so) of the strings were being used to create unique methods > on the example group (one per iteration). > > > Is this something that should be: > > a. avoided, because it''s crazy, and written differently > b, documented > c. investigated further > > I can provide a more complete example if helpful? > > Thanks, > Nick > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
nruth
2010-Jul-30 21:00 UTC
[rspec-users] rspec 1 - nested examples (in an each block) use the final block variable 4 times instead of each of the 4 once
Hi David I think this was a case of idiot-end-user (or developer) on my part, sorry about that. I''ve produced a simple example here (and figured out what I was doing wrong). http://gist.github.com/501296 I don''t think anything needs to change, though a wrapper?function (each + an inner describe) might help flag it as a possible pitfall. Thanks, Nick On Jul 29, 1:22?am, David Chelimsky <dchelim... at gmail.com> wrote:> On Jul 28, 2010, at 5:42 PM, nruth wrote: > > > > > > > Please correct me if this is fixed in Rspec 2, but in Rspec 1 I have > > hit upon the following problem (at least thrice, this time it costing > > me several hours), code is the best example: > > > I have a practice examination system where each record is one of > > several "subtests" (think tagging rather than subclassing) which is > > part of a constant string array. I want to test each''s behaviour > > indepently in my specs (since an earlier implementation used STI > > rather than tagging, and I want to test each kind of test follows the > > spec) > > > My specs use this approach in parts: > > > describe "#add_question_set_of_type" do > > ?MyModule::SUBTESTS.each do |subtest| > > > ? ?describe %Q("#{subtest}") do > > ? ?? specs using subtest > > ?end > > end > > > where SUBTESTS is a constant array of strings. > > > the problem is doing this breaks any blocks that look like > > Where are these blocks, in the outer or inner example group? Please provide a complete example, including everything I need to run and see the output you''re seeing. > > Thx > > > > > > > > > ?let(:subtest) {MyModule.subtest_to_sym(subtest)} > > > or > > > ?before(:each) do > > ? ?@subtest = MyModule.subtest_to_sym(subtest) > > ?end > > > Instead I have to avoid the @ or let examples, and use the more > > explicit (verbose): > > > ?MyModule.subtest_to_sym(subtest) > > > using the let or before @ approaches both fail. > > > Using puts within the code (for debugging purposes) I found that the > > specs were only being exposed to the final value of the array, while I > > expected (and have seen, or at least assumed from past passes) that > > all 4 (or so) of the strings were being used to create unique methods > > on the example group (one per iteration). > > > Is this something that should be: > > > a. avoided, because it''s crazy, and written differently > > b, documented > > c. investigated further > > > I can provide a more complete example if helpful? > > > Thanks, > > Nick > > _______________________________________________ > > rspec-users mailing list > > rspec-us... at rubyforge.org > >http://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
Ashley Moran
2010-Jul-30 22:13 UTC
[rspec-users] rspec 1 - nested examples (in an each block) use the final block variable 4 times instead of each of the 4 once
On 30 Jul 2010, at 10:00 PM, nruth wrote:> http://gist.github.com/501296 > > I don''t think anything needs to change, though a wrapper function > (each + an inner describe) might help flag it as a possible pitfall.Hi Nick I think the "before + ivar" pattern (below) is on its way out. At least, I consider it deprecated in my head: describe "before" do before(:each) { @foo = "bar" } before(:each) { @foo = "baz" } it "does this" do @foo.should eq "baz" end end But the new syntax does indeed let you redefine `let` blocks. describe "let" do let(:foo) { "bar" } let(:foo) { "baz" } it "does this (currently)" do foo.should eq "baz" end end Arguably, this is not the most desirable behaviour, as it leads to silent/confusing failures, as you''ve seen. Maybe it would be better if this raised an error? (Which is only an option with `let`, not with ivars.) Also, when combined with shared examples, this could conceivably be used to violate the Liskov Substitution Principle (if the LSP applies to specs). While Ruby as a language lets you redefine stuff at will, I''m not sure that''s appropriate here. I can''t imagine wanting to redefine a let, instead of factoring it out properly. Anyone got any thoughts on that? Ash -- http://www.patchspace.co.uk/ http://www.linkedin.com/in/ashleymoran
nruth
2010-Jul-31 01:08 UTC
[rspec-users] rspec 1 - nested examples (in an each block) use the final block variable 4 times instead of each of the 4 once
Hi Ash I''ve found let and before useful in different situations. I like let as a ''new feature'' but am not sure it replaces, or is necessarily superior to, @vars in all cases for specs. If I want to set up a context to run some examples in (models created with machinist, associations, etc) then the before block makes it clear that that''s the state the examples are running against, and the @vars give me a (quick and dirty?) hook to refer back to parts of that setup (e.g. associated models) in the spec examples. Of course, this can be done with let as well, but since they are lazy- evaluated you can end up with your examples running in the wrong context. For example: http://gist.github.com/501517 So after falling in love with let I''ve found sometimes it isn''t worth the overhead & it''s just quicker and tidyer to use before/@ ? I wonder whether in the end the only difference is the syntax highlighting, couple of extra lines of code or @ character, and perhaps the time / thought process in getting there. back to op briefly, I ended up with this approach for the specs instead: describe "#add_question_set_of_type(''Abstract Reasoning'')" do let(:subtest) {''Abstract Reasoning''} it_should_behave_like "adding a question set" end describe "#add_question_set_of_type(''Decision Analysis'')" do let(:subtest) {''Decision Analysis''} it_should_behave_like "adding a question set" end etc? perhaps not ideal if another test is added in later, and I wanted coverage of that too for free, but it avoided the immediate problem & is more clear what''s going on. I certainly prefer let and subject to @vars for shared examples - again both work, but I find subject & lets provide a clear interface for the shared example groups which I felt was lacking when passing @variables around. Re: error / warning message, at the same scope (i.e. an accident, as in the op) then yes that could be quite useful for spotting mistakes. I''m not so sure about in different blocks though, it''s probably intentional there (different context). Re: redefining vs refactoring - I like what you''re saying (reminds me of dry vs non-dry specs, and I find myself refactoring a lot of my old spaghetti specs lately), but wonder if there are (edge) cases where it''s useful or necessary. Maybe there''s some discussion dating back to when the feature was added. It may be stylistic though, rather than enforceable? Re: LSP, food for thought, nothing to add at present unfortunately. Not sure what angle you''re approaching shared examples from with that though, do you mean redefining lets inside of the shared example groups, or something else? Cheers, Nick On Jul 30, 11:13?pm, Ashley Moran <ashley.mo... at patchspace.co.uk> wrote:> On 30 Jul 2010, at 10:00 PM, nruth wrote: > > >http://gist.github.com/501296 > > > I don''t think anything needs to change, though a wrapper function > > (each + an inner describe) might help flag it as a possible pitfall. > > Hi Nick > > I think the "before + ivar" pattern (below) is on its way out. ?At least, I consider it deprecated in my head: > > ? describe "before" do > ? ? before(:each) { @foo = "bar" } > ? ? before(:each) { @foo = "baz" } > > ? ? it "does this" do > ? ? ? @foo.should eq "baz" > ? ? end > ? end > > But the new syntax does indeed let you redefine `let` blocks. > > ? describe "let" do > ? ? let(:foo) { "bar" } > ? ? let(:foo) { "baz" } > > ? ? it "does this (currently)" do > ? ? ? foo.should eq "baz" > ? ? end > ? end > > Arguably, this is not the most desirable behaviour, as it leads to silent/confusing failures, as you''ve seen. ?Maybe it would be better if this raised an error? ?(Which is only an option with `let`, not with ivars.) > > Also, when combined with shared examples, this could conceivably be used to violate the Liskov Substitution Principle (if the LSP applies to specs). ?While Ruby as a language lets you redefine stuff at will, I''m not sure that''s appropriate here. ?I can''t imagine wanting to redefine a let, instead of factoring it out properly. ?Anyone got any thoughts on that? > > Ash > > --http://www.patchspace.co.uk/http://www.linkedin.com/in/ashleymoran > > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
Ashley Moran
2010-Jul-31 07:48 UTC
[rspec-users] rspec 1 - nested examples (in an each block) use the final block variable 4 times instead of each of the 4 once
On 31 Jul 2010, at 2:08 AM, nruth wrote:> If I want to set up a context to run some examples in (models created > with machinist, associations, etc) then the before block makes it > clear that that''s the state the examples are running against, and the > @vars give me a (quick and dirty?) hook to refer back to parts of that > setup (e.g. associated models) in the spec examples. > > Of course, this can be done with let as well, but since they are lazy- > evaluated you can end up with your examples running in the wrong > context. For example: http://gist.github.com/501517Hi Nick I''ve been bitten by that too :) However, RSpec 2 comes to the rescue with `let!`, which evaluates the block immediately, so you don''t have to call the method to get it evaluated. It''s interesting though, I''ll have to sit down some time and think through what the semantics of `before`, `let` and `let!` mean to me...> back to op briefly, I ended up with this approach for the specs > instead: > > describe "#add_question_set_of_type(''Abstract Reasoning'')" do > let(:subtest) {''Abstract Reasoning''} > it_should_behave_like "adding a question set" > end > > describe "#add_question_set_of_type(''Decision Analysis'')" do > let(:subtest) {''Decision Analysis''} > it_should_behave_like "adding a question set" > end > > etc? > > perhaps not ideal if another test is added in later, and I wanted > coverage of that too for free, but it avoided the immediate problem & > is more clear what''s going on.Hmm, without seeing the rest of your code it''s hard to say, but this looks like a case of Control Couple. Is there a case statement inside? If not is there any reason not to have methods #add_abstract_reasoning_question_set, #add_decision_analysis_question_set? If you''re not branching on the parameter, how does the different behaviour come about?> I certainly prefer let and subject to @vars for shared examples - > again both work, but I find subject & lets provide a clear interface > for the shared example groups which I felt was lacking when passing > @variables around.I agree. I''m going to see if I can tighten this up today with a DSL to explicitly name the requirements of a shared example. (See the thread "Evaluating shared example customisation block before shared block".)> Re: error / warning message, at the same scope (i.e. an accident, as > in the op) then yes that could be quite useful for spotting mistakes. > I''m not so sure about in different blocks though, it''s probably > intentional there (different context). > > Re: redefining vs refactoring - I like what you''re saying (reminds me > of dry vs non-dry specs, and I find myself refactoring a lot of my old > spaghetti specs lately), but wonder if there are (edge) cases where > it''s useful or necessary. Maybe there''s some discussion dating back to > when the feature was added. It may be stylistic though, rather than > enforceable? > > Re: LSP, food for thought, nothing to add at present unfortunately. > Not sure what angle you''re approaching shared examples from with that > though, do you mean redefining lets inside of the shared example > groups, or something else?Redefining lets anywhere actually. The case for doing it within an example group is probably stronger, as you wrote (or can manage) all the examples yourself. I guess if RSpec raised an error when a shared example accidentally redefines something from the host group, that would be noise, and force you to change your own code unnecessarily. Hopefully, if shared examples can say "I require these things to be set up", that issues won''t arise. We''ll see :) Ash -- http://www.patchspace.co.uk/ http://www.linkedin.com/in/ashleymoran
Ashley Moran
2010-Jul-31 07:50 UTC
[rspec-users] rspec 1 - nested examples (in an each block) use the final block variable 4 times instead of each of the 4 once
On 31 Jul 2010, at 2:08 AM, nruth wrote:> Re: error / warning message, at the same scope (i.e. an accident, as > in the op) then yes that could be quite useful for spotting mistakes. > I''m not so sure about in different blocks though, it''s probably > intentional there (different context).I forgot to add: David - think it''s worth one or other of us filing a ticket for this? Is this check something that is likely to make it into RSpec? -- http://www.patchspace.co.uk/ http://www.linkedin.com/in/ashleymoran