Skip to content

Commit

Permalink
Don't trust Object#is_a? in presence of Mocks
Browse files Browse the repository at this point in the history
e9de64e broke quite a few of our tests
because since we use Sorbet, some of our mocks have to stub `is_a?`
to comply with runtime type checking.

To be fair, our stubs were incorrect and after fixing them it's fine
but the failure was happening deep in Mocha and I think it could
be handled better.

Here's a small repro of the problem we ran into:

```ruby

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  gem "minitest"
  gem "mocha"
end

require "minitest/autorun"
require "mocha/minitest"

module TypedAPI
  def self.call(user)
    raise TypeError, "Not a user" unless user.is_a?(User)
  end
end

class User
end

class BugTest < Minitest::Test
  def test_expect_mock
    user = mock("user")
    user.responds_like_instance_of(User)
    # This is wrong and shouldn't even work, but somehow it did
    # until now.
    user.stubs(:is_a? => User)
    TypedAPI.expects(:something).with(user)
    TypedAPI.call(user)
    TypedAPI.something(user)
  end
end
```

```
NoMethodError: undefined method `matches?' for #<Mock:user> which responds like #<User:0xde8>
    mocha-2.4.1/lib/mocha/mock.rb:389:in `check_responder_responds_to'
    mocha-2.4.1/lib/mocha/mock.rb:322:in `handle_method_call'
    mocha-2.4.1/lib/mocha/mock.rb:315:in `method_missing'
    mocha-2.4.1/lib/mocha/parameters_matcher.rb:21:in `block in parameters_match?'
    mocha-2.4.1/lib/mocha/parameters_matcher.rb:21:in `all?'
    mocha-2.4.1/lib/mocha/parameters_matcher.rb:21:in `parameters_match?'
    mocha-2.4.1/lib/mocha/parameters_matcher.rb:16:in `match?'
    mocha-2.4.1/lib/mocha/expectation.rb:647:in `match?'
    mocha-2.4.1/lib/mocha/expectation_list.rb:59:in `block in matching_expectations'
    mocha-2.4.1/lib/mocha/expectation_list.rb:59:in `select'
    mocha-2.4.1/lib/mocha/expectation_list.rb:59:in `matching_expectations'
    mocha-2.4.1/lib/mocha/expectation_list.rb:29:in `match_allowing_invocation'
    mocha-2.4.1/lib/mocha/mock.rb:324:in `handle_method_call'
    mocha-2.4.1/lib/mocha/stubbed_method.rb:47:in `block in define_new_method'
    /tmp/mocha.rb:35:in `test_expect_mock'
```
  • Loading branch information
byroot authored and floehopper committed Jul 18, 2024
1 parent 8bc7949 commit d57a087
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ Style/WhileUntilModifier:
Style/AccessModifierDeclarations:
Enabled: false

# `Module#===` is useful in presence of objects such as mocks
# that may have a `is_a?` implementation that lies.
Style/CaseEquality:
Enabled: false

# This is useful when using `ExecutionPoint.current` to make tests more robust
Style/Semicolon:
Enabled: false
Expand Down
4 changes: 2 additions & 2 deletions lib/mocha/parameter_matchers/instance_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ module ParameterMatchers
module InstanceMethods
# @private
def to_matcher(expectation: nil, top_level: false)
if is_a?(Base)
if Base === self
self
elsif is_a?(Hash) && top_level
elsif Hash === self && top_level
Mocha::ParameterMatchers::PositionalOrKeywordHash.new(self, expectation)
else
Mocha::ParameterMatchers::Equals.new(self)
Expand Down

0 comments on commit d57a087

Please sign in to comment.