-
-
Notifications
You must be signed in to change notification settings - Fork 357
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
SystemStackError: stack level too deep when using "and_call_original" for overwritten ".new" #1318
Comments
and_call_original
for overwritten .new
When you stub a method you override it with a new method. When you use
A sub class would work but thats not whats happening here. This is actually a loop in B / A even without our mock. e.g this:
Causes:
So we can't fix this, because even if we removed out method from the chain, it'd still be a |
There's no > B.method(:new).owner
=> #<Class:A> This (notice no mocks this time): class A
def self.new
return super if self == B
B.new
end
end
B = Class.new(A)
RSpec.describe A do
it do
A.new
end
end works as expected, class A
def self.new
RSpec::Mocks.space.proxy_for(self).reset
return super if self == B
B.new
end
end
B = Class.new(A)
RSpec.describe A do
it do
expect(A).to receive(:new).and_call_original
A.new
end
end it does! I don't know pretty much anything about rspec mocks but it's quite apparent that only while mocking with class A
def self.test
puts "I am #{self}"
B.test if self != B
end
end
B = Class.new(A)
RSpec.describe A do
it do
expect(A).to receive(:test).and_call_original
A.test
end
end It will fail. If you clear the stub with |
Actually, I'm overcomplicating this yet again. class A
def self.test
puts "I am #{self}"
end
end
B = Class.new(A)
RSpec.describe A do
it do
expect(A).to receive(:test).and_call_original
B.test
end
end outputs |
There is, it's just inherited from A. Hm, I'm willing to look into why the Your last example works fine by the way because theres no loop. |
(you'd still be better off removing the loop :P) |
It's not exactly a loop, as it's supposed to exit once it gets called from within a subclass. I do agree I'm doing a little bit of parkour (in real code |
Its a loop in that it does more than one pass through itself. Its not an infinite loop normally due to your shortcut. |
Let's just agree to call it a very short loop (or a very long one, if it's stubbed 🤔 🤔 🤔) |
Ok, so I've looked into this a bit more. What happens is we capture the method implementation of I'm not sure if can know if you're in |
Yeah, this is quite clearly visible with the last example I've posted, all the looping was unnecessary to uncover the issue, was just how I happened to find it. In theory it should be possible, but I have no idea how many issues that could bring up and how difficult it would be to implement, as I've never looked at class A
def self.whoami
puts self
end
end
B = Class.new(A)
orig = A.method(:whoami)
orig.call # => A
rebound = orig.unbind.bind(B)
rebound.call # => B Does sound like a cool challenge though 🤷♂️ |
Yeah like I said the issue is getting the outer self, |
Subject of the issue
When overwriting a class'
.new
method in order to return a subclass and expecting that method to be called with.and_call_original
, we end up with a stack level too deep.Your environment
Steps to reproduce
Expected behavior
To be fair, I realize it's far from being a common use case. I'd understand if there was no desire to fix it, as fixing it could potentially mean treating stubbing
.new
in a different way.Actual behavior
The text was updated successfully, but these errors were encountered: