Cucumber ".should contain(expected) does but fails anyway


#1

This is the step definition:

When /the xml document should have the Bank of Canada title/ do
expected = ‘Bank of Canada: Noon Foreign Exchange
Rates’
fx_doc = ForexCASource.new(@xchg_source)
puts fx_doc.xpath(’//rdf:RDF/xmlns:channel/xmlns:title’)
fx_doc.xpath(’//rdf:RDF/xmlns:channel/xmlns:title’).should
contain(expected)
end

When I exercise the feature I see this:

When the currency exchange retrieval script runs
  #

features/app/models/currency_exchange_rates/step_definitions/currency_exchange_rates_steps.rb:9

Bank of Canada: Noon Foreign Exchange Rates <= from puts
Then the xml document should have the Bank of Canada title
  #

features/app/models/currency_exchange_rates/step_definitions/currency_exchange_rates_steps.rb:13

  expected the following element's content to include

“Bank of Canada: Noon Foreign Exchange Rates”:

  Bank of Canada: Noon Foreign Exchange Rates

(Spec::Expectations::ExpectationNotMetError)
./features/app/models/currency_exchange_rates/step_definitions/currency_exchange_rates_steps.rb:17:in
/the xml document should have the Bank of Canada title/' features/app/models/currency_exchange_rates/currency_exchange_rates.feature:10:inThen the xml document should have the Bank of Canada title’

1 scenario
1 failed step
2 passed steps

Now, other than the double quotes surrounding the comparison string in
the failure message there is no difference between what I expected and
what was returned. Nonetheless the specification failed? What am I
doing wrong?


#2

When I change the test to:

expected = ‘Bank of Canada: Noon Foreign Exchange
Rates’
fx_doc.xpath(’//rdf:RDF/xmlns:channel/xmlns:title’).should =~ expected

Then I see this instead:

expected: “Bank of Canada: Noon Foreign Exchange
Rates”,
got: Bank of Canada: Noon Foreign Exchange Rates
(using =~)

What is adding the double quotes?


#3

On Wed, Apr 22, 2009 at 10:02 AM, James B. removed_email_address@domain.invalid
wrote:

Rates",
got: Bank of Canada: Noon Foreign Exchange Rates
(using =~)

What is adding the double quotes?

Ruby. Try this in irb:

‘this’
=> “this”


#4

David C. wrote:

Ruby. Try this in irb:

‘this’
=> “this”

This passes:

assert_equal (expected,
fx_doc.xpath(’//rdf:RDF/xmlns:channel/xmlns:title’).to_s,
“#{expected} not found”)

This does not:

fx_doc.xpath(’//rdf:RDF/xmlns:channel/xmlns:title’).to_s.should
contain(expected)

Or this:

fx_doc.xpath(’//rdf:RDF/xmlns:channel/xmlns:title’).to_s.should
equal(expected)

gives this lovely, and illogical error:

  expected "<title>Bank of Canada: Noon Foreign Exchange 

Rates"
got “Bank of Canada: Noon Foreign Exchange
Rates”

  (compared using equal?)
   (Spec::Expectations::ExpectationNotMetError)

Do not even start on how equal? in Ruby is defined completely at odds
with common usage. I am just pointing out that this type of thing
definitely violates the principal of least surprise.

So, test unit it is.


#5

On Wed, Apr 22, 2009 at 11:08 AM, James B. removed_email_address@domain.invalid
wrote:

           fx_doc.xpath('//rdf:RDF/xmlns:channel/xmlns:title').to_s,
           equal(expected)

Do not even start on how equal? in Ruby is defined completely at odds
with common usage. I am just pointing out that this type of thing
definitely violates the principal of least surprise.

Yeah - this is one of those decisions I made early on that I wish I
could take back, but doing so would break a lot of people’s specs.
Maybe we should look into changing in it in rspec-2. The original
rspec had should_equal (equivalence) and should_be (object identity).

In the mean time, I’m not sure what better message we can give beyond
“compared using .equal?” without getting into a long treatise on
equality in Ruby, which seems out of place in a failure message.

So, test unit it is.

The original issue you posted is with the contain matcher, which is in
webrat, not rspec. Why it’s not working, I’m not quite sure, but if
you’re going to throw out the baby with the bath water, you might
consider figuring out who the parents are :slight_smile:

Looking at the webrat code for the contain matcher, it uses Nokogiri
under the hood. Not sure what’s happening there, but you might try
have_tag instead:

should have_tag(“title”, “Bank of Canada: Noon Foreign Exchange Rates”)

Let me know if it works.

Cheers,
David


#6

David C. wrote:

The original issue you posted is with the contain matcher, which is in
webrat, not rspec. Why it’s not working, I’m not quite sure, but if
you’re going to throw out the baby with the bath water, you might
consider figuring out who the parents are :slight_smile:

Yes, I figured out that #contain was the culprit when I discovered that
this worked:

fx_doc.xpath(’//rdf:RDF/xmlns:channel/xmlns:title’).to_s.should
==(expected)

Ugly, to me, but it works so the problem is not RSpec. Strangely,
however, this construct also failed:

fx_doc.xpath(’//rdf:RDF/xmlns:channel/xmlns:title’).to_s.should
=~(expected)

I am not throwing out RSpec or using it any less. I just had to get
around a specific problem and took the first route I found that worked.

In the mean time, I’m not sure what better message we can give beyond
“compared using .equal?” without getting into a long treatise on
equality in Ruby, which seems out of place in a failure message.

How about:

    expected "<title>Bank of Canada: Noon Foreign Exchange

Rates"
got “Bank of Canada: Noon Foreign Exchange
Rates”

  (equal?: expected object is not the object returned, did you mean 

‘==’)
(Spec::Expectations::ExpectationNotMetError)


#7

On Wed, Apr 22, 2009 at 1:45 PM, James B. removed_email_address@domain.invalid
wrote:

this worked:
I am not throwing out RSpec or using it any less. I just had to get
got “Bank of Canada: Noon Foreign Exchange
Rates”

 (equal?: expected object is not the object returned, did you mean

‘==’)
(Spec::Expectations::ExpectationNotMetError)

I can live with that. Do you want to make a patch? If not I’ll just add
it.


#8

David C. wrote:

On Wed, Apr 22, 2009 at 1:45 PM, James B. removed_email_address@domain.invalid
wrote:

this worked:
I am not throwing out RSpec or using it any less. �I just had to get
� � � � � � got “Bank of Canada: Noon Foreign Exchange
Rates”

� � �(equal?: expected object is not the object returned, did you mean
‘==’)
� � � (Spec::Expectations::ExpectationNotMetError)

I can live with that. Do you want to make a patch? If not I’ll just add
it.

I’ll try and make a patch first. If that does not work out then I will
beg a boon of you to do it.

I think that the error message should change too. Instead of:

    expected "<title>Bank of Canada: Noon Foreign Exchange

Rates"
got “Bank of Canada: Noon Foreign Exchange
Rates”

An equal? failure probably should return the metaclass like:

    expected "#<Class:#<String:0x2b6950315300>>"
         got "#<Class:#<String:0x2b695026e898>>"

yada yada…

WDYT?


#9

On Wed, Apr 22, 2009 at 2:09 PM, James B. removed_email_address@domain.invalid
wrote:

� � � (Spec::Expectations::ExpectationNotMetError)
Rates"
WDYT?
I like the idea, though I think it’s helpful to also have the strings
in the case of String objects. But having the class and object id
would really help tell the story we’re looking for.

wdYt?


#10

David C. wrote:

On Wed, Apr 22, 2009 at 2:09 PM, James B. removed_email_address@domain.invalid
wrote:

� � � (Spec::Expectations::ExpectationNotMetError)
Rates"
WDYT?
I like the idea, though I think it’s helpful to also have the strings
in the case of String objects. But having the class and object id
would really help tell the story we’re looking for.

wdYt?

After I posted I reconsidered the situation. When equal? is the
comparison then the first step should be to check for identical objects
(duhh). If that fails then do a supplementary == check. If that passes
then append the ", did you mean ‘==’ notice and display the values
together with the object.metaclass. Otherwise, just display the objects
are different warning and the object.metaclass for each.

However, on reconsideration again, it seems best to skip the second
check and just display this sort of thing for all equal? failures:

expected: “#<Class:#String:0x2b6950315300> => ‘Bank of
Canada: Noon Foreign Exchange Rates’”
got: “#<Class:#String:0x2b695026e898> => ‘Bank of
Canada: Noon Foreign Exchange Rates’”

(equal?: expected object is not the object returned, did you mean
‘==’)
(Spec::Expectations::ExpectationNotMetError)

Of course, to support this outside Rails, RSpec will have to provide and
require the metaclass.rb (shamelessly stolen from Rails).

unless Object.respond_to? :metaclass do
class Object
# Get object’s meta (ghost, eigenclass, singleton) class
def metaclass
class << self
self
end
end
end
end

Thoughts?


#11

David C. wrote:

I like the idea, though I think it’s helpful to also have the strings
in the case of String objects. But having the class and object id
would really help tell the story we’re looking for.

The relationship between “#<Class:#String:0x2b22de53e018>” and the
object’s id is rather bizarre.

irb(main):022:0> x.metaclass
=> #<Class:#String:0x2b22de53e018>
irb(main):023:0> x.object_id
=> 23714379460620
irb(main):024:0> 0x2b22de53e018.to_s(10)
=> “47428758921240”
irb(main):025:0> 0x2b22de53e018.to_i
=> 47428758921240
irb(main):026:0> 23714379460620 * 2
=> 47428758921240

Obviously an extra bit is getting set somewhere; or unset in #object_id.


#12

+1. I like it.


John G. RADSoft / Better Software Faster
removed_email_address@domain.invalid Lean/Agile/XP/Scrum Coaching and
Training
http://www.radsoft.com Ruby on Rails and Java Solutions


#13

David C. wrote:

On Wed, Apr 22, 2009 at 2:09 PM, James B. removed_email_address@domain.invalid
wrote:

� � � (Spec::Expectations::ExpectationNotMetError)
Rates"
WDYT?
I like the idea, though I think it’s helpful to also have the strings
in the case of String objects. But having the class and object id
would really help tell the story we’re looking for.

wdYt?

I have come up with this spec. Before I poke at the code itself I would
like you to comment.

  it "should display object and value, expected and actual, on 

#failure_message" do
target = 1
matcher = equal(“1”)
matcher.matches?(target)
matcher.failure_message_for_should.should ==
“\n” +
“expected “#{matcher.metaclass} => 1”\n” +
" got “#{target.metaclass} => 1\n” +
" \n(compared using equal?, did you mean ‘==’)\n"
end


#14

Another question: Where should metaclass.rb go; lib? lib/spec? or should
the method be put inside the spec_helper file?


#15

On Thu, Apr 23, 2009 at 11:08 AM, James B. removed_email_address@domain.invalid
wrote:

Another question: Where should metaclass.rb go; lib? lib/spec? or should
the method be put inside the spec_helper file?

This is a utility for matchers to give good messages, so I’d put it in
lib/spec/matchers

I don’t think we should call it metaclass though, but I’m not sure
what I do want to call it. Metaclass != eigenclass, which is what
this really is, so maybe eigenclass.rb - don’t let that hang you up
though, I can always change the names after.

Thanks!

David


#16

David C. wrote:

I don’t think we should call it metaclass though, but I’m not sure
what I do want to call it. Metaclass != eigenclass, which is what
this really is, so maybe eigenclass.rb - don’t let that hang you up
though, I can always change the names after.

The thing is, Rails already defines #metaclass. So, I thought to check
for that method before extending Object with my own. If
Object.respond_to? :metaclass == true then we are in Rails, or some
other framework that provides the same thing, and so we need not provide
our own. Otherwise we add it.

Comments?


#17

David C. wrote:

On Thu, Apr 23, 2009 at 1:56 PM, James B. removed_email_address@domain.invalid
wrote:

for that method before extending Object with my own. If
Object.respond_to? :metaclass == true then we are in Rails, or some
other framework that provides the same thing, and so we need not provide
our own. �Otherwise we add it.

Comments?

I’d rather always define our own so the results are consistent from
RSpec regardless of other frameworks in the midst. Does that make
sense to you?

Yes.


#18

On Thu, Apr 23, 2009 at 1:56 PM, James B. removed_email_address@domain.invalid
wrote:

for that method before extending Object with my own. If
Object.respond_to? :metaclass == true then we are in Rails, or some
other framework that provides the same thing, and so we need not provide
our own. Otherwise we add it.

Comments?

I’d rather always define our own so the results are consistent from
RSpec regardless of other frameworks in the midst. Does that make
sense to you?


#19

David C. wrote:

I’d rather always define our own so the results are consistent from
RSpec regardless of other frameworks in the midst. Does that make
sense to you?

Instead of metaclass how about object_handle ?

wdt?


#20

On Thu, Apr 23, 2009 at 2:26 PM, James B. removed_email_address@domain.invalid
wrote:

David C. wrote:

I’d rather always define our own so the results are consistent from
RSpec regardless of other frameworks in the midst. Does that make
sense to you?

Instead of metaclass how about object_handle ?

wdt?

Go for it. I think that’s good enough for now. It’s for internal
consumption only. Also, I’m better at making naming decisions when I
see things in context, I may change it later if I come up w/ something
that speaks to me more clearly.

Good?