Skip to content
This repository has been archived by the owner on Nov 30, 2024. It is now read-only.

raise ArgumentError if object cannot be proxied #1357

Merged
merged 4 commits into from
Dec 12, 2020
Merged

Conversation

zhisme
Copy link
Contributor

@zhisme zhisme commented Dec 6, 2020

fixes #1356

@zhisme
Copy link
Contributor Author

zhisme commented Dec 6, 2020

@pirj can you take a look?

however I cannot understand why travis CI failed, I will try to reproduce with the given ruby versions on localhost to identify what's wrong

Copy link
Member

@JonRowe JonRowe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling this, some feedback

spec/rspec/mocks/space_spec.rb Outdated Show resolved Hide resolved
lib/rspec/mocks/proxy.rb Outdated Show resolved Hide resolved
spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb Outdated Show resolved Hide resolved
spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb Outdated Show resolved Hide resolved
@zhisme
Copy link
Contributor Author

zhisme commented Dec 6, 2020

thanks @JonRowe will update

@zhisme zhisme requested a review from JonRowe December 6, 2020 13:48
@zhisme
Copy link
Contributor Author

zhisme commented Dec 10, 2020

I couldn't find changelog for ruby 2.0.0, but surely in previous ruby versions symbols where not frozen by default, that's why travis was failing for older ruby versions.
consider this example, I reproduced and fixed with ruby 1.9.3

irb(main):001:0> RUBY_VERSION
=> "1.9.3"
irb(main):002:0> :subject.frozen?
=> false

@zhisme
Copy link
Contributor Author

zhisme commented Dec 10, 2020

@pirj @JonRowe and I see on another PR #1349 you planning to drop ruby < 2.3, maybe there is no need to adjust this code to be compatible with older ruby versions. what do you think?

@JonRowe
Copy link
Member

JonRowe commented Dec 11, 2020

@zhisme If you wish to target this off 4-0-dev for release in 4.0.0 you are welcome to drop the compatibility code, we plan to release around the launch of Ruby 3.0.0 but there is no guaranteed timeline for it. I will probably backport this to main however for release in a bugpatch in the 3.x series.

lib/rspec/mocks/proxy.rb Outdated Show resolved Hide resolved
@zhisme
Copy link
Contributor Author

zhisme commented Dec 11, 2020

@JonRowe any idea of travis-ci build error?
And other seems like failed due to this one. Need your help

Failure/Error: require 'thread_order'
LoadError:
  cannot load such file -- thread_order

@JonRowe JonRowe merged commit 2d12304 into rspec:main Dec 12, 2020
JonRowe added a commit that referenced this pull request Dec 12, 2020
@JonRowe
Copy link
Member

JonRowe commented Dec 12, 2020

Thanks, don't worry about Travis we're phasing it out as its become really unreliable since they essentially dropped open source.

JonRowe added a commit that referenced this pull request Dec 15, 2020
raise ArgumentError if object cannot be proxied
JonRowe added a commit that referenced this pull request Dec 15, 2020
JonRowe added a commit that referenced this pull request Dec 17, 2020
raise ArgumentError if object cannot be proxied
JonRowe added a commit that referenced this pull request Dec 17, 2020
JonRowe added a commit that referenced this pull request Dec 27, 2020
raise ArgumentError if object cannot be proxied
JonRowe added a commit that referenced this pull request Dec 27, 2020
@keegangroth
Copy link
Contributor

Hi, I've just pulled this in and am getting test failures in areas where I've stubbed destroyed ActiveRecord objects, which freezes them. I'm not understanding why rspec should refuse to stub these objects and I'm not seeing any explanation here, but hoping someone could explain it to me?

@JonRowe
Copy link
Member

JonRowe commented Jan 4, 2021

@keegangroth Frozen objects cannot be modified, which is required to stub them.

@keegangroth
Copy link
Contributor

keegangroth commented Jan 4, 2021

OK, I think there's something specific to ActiveRecord here, because old and new versions of rspec both throw errors for Hash objects. I'll see if I can come up with a trivial example for activerecord. Here's an example with Hash that behaves more or less the same both before and after this change.

describe 'frozen stubbing' do
  let(:h) { Hash.new }
  let(:error) { StandardError.new 'mine' }

  context 'expect_any_instance_of' do
    before do
      expect_any_instance_of(Hash).to receive(:map).and_raise(error)
    end

    # fails with either
    # expected #<StandardError: mine>, got #<ArgumentError: Cannot proxy frozen objects, rspec-mocks relies on proxies for method stubbing and expectations. Hash: {}>
    # or 
    # expected #<StandardError: mine>, got #<FrozenError: can't modify frozen class/module>
    it 'fails' do
      h.freeze
      expect{h.map}.to raise_error(error)
    end
  end

  context 'instance stub' do
    before do
      expect(h).to receive(:map).and_raise(error)
    end

    it 'works' do
      h.freeze
      expect{h.map}.to raise_error(error)
    end
  end
end

@keegangroth
Copy link
Contributor

keegangroth commented Jan 4, 2021

The behavior has definitely changed for destroyed ActiveRecord Models. I don't know what makes their version of freezing different from hash. Trivial example using rails to do the db setup:

# frozen_string_literal: true

require "bundler/inline"

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

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activerecord", "6.1.0"
  gem "rspec", "3.10.0"
  #gem "rspec-mocks", "3.10.0" # works find
  gem "rspec-mocks", "3.10.1" # fails
  gem "activerecord-jdbcsqlite3-adapter", "61.0", platform: :jruby
  gem "sqlite3", platform: :mri
end

require "active_record"
require "rspec"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")

ActiveRecord::Schema.define do
  create_table :posts, force: true
end

class Post < ActiveRecord::Base
  def my_method
    10
  end
end

RSpec.describe Post do
  let(:p) { described_class.create! }
  let(:error) { StandardError.new 'mine' }

  context 'expect_any_instance_of' do
    before do
      expect_any_instance_of(described_class).to receive(:my_method).and_raise(error)
    end

    it 'fails' do
      p.destroy! # "freezes" the instance
      expect{p.my_method}.to raise_error(error)
    end
  end

  context 'instance stub' do
    before do
      expect(p).to receive(:my_method).and_raise(error)
    end

    it 'works' do
      p.destroy! # "freezes" the instance
      expect{p.my_method}.to raise_error(error)
    end
  end
end

result on rspec-mocks 3.10.0:

Finished in 0.47361 seconds (files took 5.57 seconds to load)
2 examples, 0 failures

result on rspec-mocks 3.10.1:

Failures:

  1) Post expect_any_instance_of fails
     Failure/Error: expect{p.my_method}.to raise_error(error)

       expected #<StandardError: mine>, got #<ArgumentError: Cannot proxy frozen objects, rspec-mocks relies on proxies for method stubbing and expectations.> with backtrace:
         # ./freeze_spec.rb:46:in `block in <main>'
         # ./freeze_spec.rb:46:in `block in <main>'
     # ./freeze_spec.rb:46:in `block in <main>'

Finished in 0.17197 seconds (files took 5.14 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./freeze_spec.rb:44 # Post expect_any_instance_of fails

Probably warrants it's own (new) issue? Kind of niche, but it's now a major headache to test certain kinds of functionality in active record (I actually haven't yet figured out the work around for my particular case).

@flugsio
Copy link

flugsio commented Jan 11, 2021

The activerecord models are not really frozen, they override that method and delegates it to the attributes, which are frozen when the record is destroyed. https://github.com/rails/rails/blob/914caca2d31bd753f47f9168f2a375921d9e91cc/activerecord/lib/active_record/core.rb#L618

def frozen?
  @attributes.frozen?
end

I'm not sure if there's a better way to check the real frozen status for the model itself.

@JonRowe
Copy link
Member

JonRowe commented Jan 11, 2021

Feel free to raise that as a bug with the Rails team, if an object isn't actually frozen it probably shouldn't return that it is.

@keegangroth
Copy link
Contributor

keegangroth commented Jan 11, 2021

I don't mind raising this with rails, but does any one really think they're going to change their interface for this? Seems like there are some pretty simple alternatives here in rspec such as just catching the FrozenError (which looks like it was always raised in these cases) and re-raising with the appropriate message.

@JonRowe
Copy link
Member

JonRowe commented Jan 11, 2021

They might not be aware they are breaking an expected Ruby behaviour, they might have a reason for it being the way it is, I don't know, but currently these objects don't behave as they should by the standards Ruby sets, its worth finding out if this is blocking you, equally If you'd like to create an alternative implementation for this warning feel free.

@keegangroth
Copy link
Contributor

#1401 for what it's worth

dentarg added a commit to dentarg/influxdb-ruby that referenced this pull request Feb 3, 2021
Fails like this with rspec-mocks >=3.10.1 because we set logger to
false, and false is frozen.

      1) InfluxDB::Logging when logging is disabled does not log
         Failure/Error: expect(InfluxDB::Logging.logger).not_to receive(:debug)

         ArgumentError:
           Cannot proxy frozen objects, rspec-mocks relies on proxies for method stubbing and expectations.
         # ./spec/influxdb/logging_spec.rb:42:in `block (3 levels) in <top (required)>'
         # ./spec/influxdb/logging_spec.rb:19:in `block (2 levels) in <top (required)>'

Related links

- rspec/rspec-mocks#1357
- https://github.com/rspec/rspec-mocks/blob/v3.10.2/Changelog.md
yujinakayama pushed a commit to yujinakayama/rspec-monorepo that referenced this pull request Oct 6, 2021
raise ArgumentError if object cannot be proxied

---
This commit was imported from rspec/rspec-mocks@2d12304.
yujinakayama pushed a commit to yujinakayama/rspec-monorepo that referenced this pull request Oct 6, 2021
yujinakayama pushed a commit to yujinakayama/rspec-monorepo that referenced this pull request Oct 6, 2021
raise ArgumentError if object cannot be proxied

---
This commit was imported from rspec/rspec-mocks@e76b942.
yujinakayama pushed a commit to yujinakayama/rspec-monorepo that referenced this pull request Oct 6, 2021
yujinakayama pushed a commit to yujinakayama/rspec-monorepo that referenced this pull request Oct 19, 2021
raise ArgumentError if object cannot be proxied

---
This commit was imported from rspec/rspec-mocks@bee61ba.
yujinakayama pushed a commit to yujinakayama/rspec-monorepo that referenced this pull request Oct 19, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TypeError: can't define singleton
4 participants