Open pucinsk opened 7 months ago
I’m away from my computer. Wondering if ‘receive(:foo_bar)’ and ‘receive_alias(:foo_bar)’ would return instances of the same class? Why would there be such a difference?
It seems that something is wrong with RSpec::Matchers::AliasedMatcher
matcher. ActiveSupport
seems to override HaveReceived
methods.
I'm pasting some output from debugging session what I found, it might be helpful.
[1] pry(#<RSpec::ExampleGroups::Foo>)> have_received_alias(:foo_bar).class
=> RSpec::Matchers::AliasedMatcher
[2] pry(#<RSpec::ExampleGroups::Foo>)> have_received_alias(:foo_bar).class.superclass
=> RSpec::Matchers::MatcherDelegator
[3] pry(#<RSpec::ExampleGroups::Foo>)> have_received(:foo_bar).class
=> RSpec::Mocks::Matchers::HaveReceived
[4] pry(#<RSpec::ExampleGroups::Foo>)> have_received(:foo_bar).class.superclass
=> Object
...
[7] pry(#<RSpec::ExampleGroups::Foo>)> have_received_alias(:foo_bar).inspect
=> "#<RSpec::Mocks::Matchers::HaveReceived:0x0000000162438768 @method_name=:foo_bar, @block=nil, @constraints=[], @subject=nil>"
[8] pry(#<RSpec::ExampleGroups::Foo>)> have_received(:foo_bar).inspect
=> "#<RSpec::Mocks::Matchers::HaveReceived:0x00000001624348e8 @method_name=:foo_bar, @block=nil, @constraints=[], @subject=nil>"
[6] pry(#<RSpec::ExampleGroups::Foo>)> show-method have_received_alias(:foo_bar).with
From: gems/ruby/3.2.0/gems/activesupport-7.1.3.2/lib/active_support/core_ext/object/with.rb:4:
[5] pry(#<RSpec::ExampleGroups::Foo>)> show-method have_received(:foo_bar).with
From: gems/ruby/3.2.0/gems/rspec-mocks-3.13.0/lib/rspec/mocks/matchers/have_received.rb:53:
My current workaround:
class RailsAliasedMatcherPatched < RSpec::Matchers::AliasedMatcherWithOperatorSupport
def with(*, **)
base_matcher.__send__(:with, *, **)
end
end
RSpec::Matchers.alias_matcher :have_received_alias, :have_received, klass: RailsAliasedMatcherPatched
RSpec::Matchers.alias_matcher :receive_alias, :receive, klass: RailsAliasedMatcherPatched
I'm curious, what runs first in your setup, the Rails' patch to Object that
defines with
, or ours BaseDelegator
that relies on the Object to be
already patched.
Wondering if this can be load order-dependent.
I could not reproduce the issue with this (similar to https://github.com/rails/rails/issues/49958):
# frozen_string_literal: true
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem "sqlite3"
gem 'rails', '~> 7.1'
gem 'rspec-rails'
end
require "rails/all"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database:
":memory:")
require 'rspec/rails'
require 'rspec/autorun'
RSpec::Matchers.alias_matcher :receive_alias, :receive
RSpec.describe 'alias_matcher' do
it 'works' do
d = double
expect(d).to receive(:foo).with(1)
d.foo(1)
end
it 'does not' do
d = double
expect(d).to receive_alias(:foo).with(1)
d.foo(1)
end
end
Subject of the issue
Rails 7.1 added Object#with which conflicts with
receive
andhave_received
that define a #with method (chain :with) when used via an alias_matcher.Related issue: https://github.com/rspec/rspec-expectations/issues/1437 (solved)
Your environment
Steps to reproduce
Expected behavior
Neither assert should raise an
NoMethodError
Actual behavior
Arguments passed for
with
method are treated as Object methods.