Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Message expectation unexpectedly failing when calling method with an object defining #== #914

Closed
carhartl opened this issue Mar 25, 2015 · 4 comments

Comments

@carhartl
Copy link

Using RSpec 3.2.2 given the following example code:

class Foo
  attr_reader :a, :b

  def initialize(a:, b:)
    @a = a
    @b = b
  end

  def ==(other)
    a == other.a && b == other.b
  end
end

class Bar
  def do_something_with(foo)
    puts foo
  end
end


describe Bar do
  let(:foo) { Foo.new(a: "a", b: "b") }

  it "testing argument matcher" do
    expect_any_instance_of(Bar).to receive(:do_something_with)
    Bar.new.do_something_with(foo)
  end
end

I am seeing the example unexpectedly fail:

Failures:

  1) Bar testing argument matcher
     Failure/Error: a == other.a && b == other.b
     NoMethodError:
       undefined method `a' for #<RSpec::Mocks::ArgumentMatchers::AnyArgMatcher:0x007f8ad98c2768>
     # ./with_spec.rb:10:in `=='
     # ./with_spec.rb:26:in `block (2 levels) in <top (required)>'

As soon as I comment out Foo#== the spec will pass. Is this something I am doing wrong? Is it bad style/unexpected to redefine #==? I also tried to define my own argument matcher for foos in order to workaround this, but without luck.

I wasn't seeing this with an older version of RSpec.

@carhartl
Copy link
Author

PS: I understood from the documentation, that "Arguments that are passed to with are compared with actual arguments received using ==", and even though with isn't even involved here, the any arguments matcher seems to be applied internally when omitting it?

@carhartl
Copy link
Author

... and using an argument matcher explicitly is producing a similar error:

it "testing argument matcher" do
  expect_any_instance_of(Bar).to receive(:do_something_with).with(kind_of(Foo))
  Bar.new.do_something_with(foo)
end
Failures:

  1) Bar testing argument matcher
     Failure/Error: a == other.a && b == other.b
     NoMethodError:
       undefined method `a' for #<RSpec::Mocks::ArgumentMatchers::KindOf:0x007f973c216760>
     # ./with_spec.rb:10:in `=='
     # ./with_spec.rb:26:in `block (2 levels) in <top (required)>'

@fables-tales
Copy link
Member

This is not an RSpec bug. Consider:

class Foo
  attr_reader :a, :b

  def initialize(a:, b:)
    @a = a
    @b = b
  end

  def ==(other)
    a == other.a && b == other.b
  end
end
Foo.new(a: 1, b: 2) == Object.new
#NoMethodError: undefined method `a' for #<Object:0x007f803913aab8>
#   from (irb):10:in `=='
#   from (irb):14
#   from /Users/sam/.rubies/ruby-2.1.5/bin/irb:11:in `<main>'

The problem here is that your Foo object is not playing well with other objects in the ruby system. Your object should be able to equality check itself against any other object that is passed to it. This is not the case, your object currently only can equality check itself against other objects that respond to #a and #b. You should check that other is of the correct class, or responds to those methods. Then your equality method will work and your tests will continue to function.

@carhartl
Copy link
Author

Aaaaaarg, makes total sense, thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants