On Aug 25, 2011, at 11:40 AM, Lenny Marks wrote:
> One of the things that always annoys me when I write/use typical macros in
my specs is how the backtrace from failures don''t include the caller of
the macro. Not a huge deal because the example name can be used to track it
down, but I lose the ability to do things like click on the line from the
backtrace in my IDE(I use RubyMine). I find that feature to be a nice time
saver. I''ve never heard any mention of this bothering anybody so maybe
I''m just being pedantic but tweaking things so that the actual example
implementation is yielded by the caller seems like a better style to me because
it solves that problem. Just curious if anybody else has any thoughts one way or
the other. I''ve been leaning toward the latter when possible.
>
> Full example at:
> https://gist.github.com/1169172
>
> ex. When the example below fails, line 17 is significant
>
> 5 describe Email do
> 6 describe ''.normalize'', '' should strip
email down to core address'' do
> 7 def self.normalized(original, options)
> 8 it %{normalizes ''#{original}'' as
''#{options[:should_be]}''} do
> 9 Email.normalize(original).should == options[:should_be]
> 10 end
> 11 end
> .....
> 16 describe ''it strips whitespace'' do
> 17 normalized('' joe at somewhere.com'',
:should_be => ''joe at somewhere.com'')
> 18 end
>
> Failures:
>
> 1) Email.normalize should strip email down to core address it strips
whitespace normalizes '' joe at somewhere.com'' as
''joe at somewhere.com''
> Failure/Error: Email.normalize(original).should == options[:should_be]
> expected: "joe at somewhere.com"
> got: " joe at somewhere.com" (using ==)
> # org/jruby/RubyProc.java:268:in `call''
> # ./spec/lib/email_spec.rb:9:in `normalized''
> # org/jruby/RubyKernel.java:2028:in `instance_eval''
If you run this with the --backtrace flag, you should see line 17 as well.
> Compare to this where the significant lines are present in the backtrace
>
> describe Email do
> 6 describe ''.normalize'', '' should strip
email down to core address'' do
> 7 def self.normalized(original, &blk)
> 8 describe "''#{original}''" do
> 9 subject { Email.normalize(original) }
> 10 it { instance_eval(&blk) }
> 11 end
> 12 end
> ......
> 17 describe ''it strips whitespace'' do
> 18 normalized('' joe at somewhere.com'') {
should == ''joe at somewhere.com'' }
> 19 end
>
> Failures:
>
> 1) Email.normalize should strip email down to core address it strips
whitespace '' joe at somewhere.com''
> Failure/Error: normalized('' joe at somewhere.com'')
{ should == ''joe at somewhere.com'' }
> expected: "joe at somewhere.com"
> got: " joe at somewhere.com" (using ==)
> # org/jruby/RubyProc.java:268:in `call''
> # ./spec/lib/email_spec.rb:18:in `(root)''
> # org/jruby/RubyKernel.java:2028:in `instance_eval''
> # ./spec/lib/email_spec.rb:10:in `normalized''
> # org/jruby/RubyKernel.java:2028:in `instance_eval''
Interesting that that happens. I''m not clear on why. Regardless, this
macro appraoch creates a lot of distance between the reader and the code that is
being specified. Personally, I find this much easier to read:
describe Email do
describe "#normalize" do
it "strips whitespace" do
Email.normalize('' joe at somewhere.com'').should
eq(''joe at somewhere.com'')
end
end
end
... and it would give exactly the spec output and failure message that I want.
Don''t get me wrong - I''m all for sharing code where it
provides a benefit that outweighs the loss of proximity, but I would typically
do that with custom matchers, shared examples or simple helper methods (at the
example level rather than the group level).
FWIW,
David