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