-
-
Notifications
You must be signed in to change notification settings - Fork 158
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
[Bug] Recent changes to Mock respond_to?
behaviour causing NoMethodError
#580
Comments
Many thanks for the bug report and for taking the time to reproduce and dig into the problem. I've had a quick look and I've found the commit where the change was originally introduced and the commit note sounds suspiciously closely related to your issue! As soon as I have time, I'll do some more investigation and get back to you. |
I don't think the way you've described the condition in what you've said is quite correct. The code that was removed from
The commit note in 6416b74 suggests the |
Hi @floehopper ! Thanks for the speedy response and for digging into this, it's much appreciated! ❤️
Oh, yes you're absolutely right! Sorry, I glanced through that code too quickly 🤦♀️
I believe it actually does so that it can handle attribute methods -- aka here. The arity is -2 though, so the same logic applies. My assessment was the same as yours -- prior to v1.15, we were ignoring the 6416b74 is an interesting find! Funny that it also had to do with |
Doh! Yes, you're quite right - I don't know how I missed that! 🤦♂️ |
The regression was inadvertently introduced in this commit [1] and indirectly led to this issue [2]. I've attempted to thoroughly characterise the pre-regression behaviour of Mock#responds_like in a new acceptance test, RespondsLikeTest, in order to reduce the chances of any such regressions occurring in the future. I've also added another acceptance test, ArrayFlattenTest, which reproduced the issue in [2] until the regression was fixed. Investigating this has made me realise that the behaviour of Mock#responds_like in combination with protected & private methods on the responder was not specified in the documentation. I've attempted to reverse engineer the documentation so that it more fully describes the pre-regression behaviour. [1]: 365a734 [2]: #580
The regression was inadvertently introduced in this commit [1] and indirectly led to this issue [2]. I've attempted to thoroughly characterise the pre-regression behaviour of Mock#responds_like in a new acceptance test, RespondsLikeTest, in order to reduce the chances of any such regressions occurring in the future. I've also added another acceptance test, ArrayFlattenTest, which reproduced the issue in [2] until the regression was fixed. Investigating this has made me realise that the behaviour of Mock#responds_like in combination with protected & private methods on the responder was not specified in the documentation. I've attempted to reverse engineer the documentation so that it more fully describes the pre-regression behaviour. [1]: 365a734 [2]: #580
The regression was inadvertently introduced in this commit [1] and indirectly led to this issue [2]. I've attempted to thoroughly characterise the pre-regression behaviour of Mock#responds_like in a new acceptance test, RespondsLikeTest, in order to reduce the chances of any such regressions occurring in the future. I've also added another acceptance test, ArrayFlattenTest, which reproduced the issue in [2] until the regression was fixed. Investigating this has made me realise that the behaviour of Mock#responds_like in combination with protected & private methods on the responder was not specified in the documentation. I've attempted to reverse engineer the documentation so that it more fully describes the pre-regression behaviour. Note that the CI build for Ruby v1.9 is using an old version of Minitest which doesn't work with the RespondsLikeTest and since support for Ruby v1.9 was dropped in Mocha v2, I don't think it's worth the effort to sort this out so I've just disabled the test for Ruby v1.9. [1]: 365a734 [2]: #580
The regression was inadvertently introduced in this commit [1] and indirectly led to this issue [2]. I've attempted to thoroughly characterise the pre-regression behaviour of Mock#responds_like in a new acceptance test, RespondsLikeTest, in order to reduce the chances of any such regressions occurring in the future. I've also added another acceptance test, ArrayFlattenTest, which reproduced the issue in [2] until the regression was fixed. Investigating this has made me realise that the behaviour of Mock#responds_like in combination with protected & private methods on the responder was not specified in the documentation. I've attempted to reverse engineer the documentation so that it more fully describes the pre-regression behaviour. Note that the CI build for Ruby v1.9 is using an old version of Minitest which doesn't work with the RespondsLikeTest and since support for Ruby v1.9 was dropped in Mocha v2, I don't think it's worth the effort to sort this out so I've just disabled the test for Ruby v1.9. [1]: 365a734 [2]: #580
Sorry for the delay in getting back to you. I have continued to investigate this issue, but it's opened a bit of a can of worms! 😂 I've opened #583 as a first stab at a fix, but I still have a bunch of questions I need to resolve. However, in the meantime it would be great if you could try changing your
|
Hey @floehopper, no worries at all! That fixes the test indeed 👍 I know that deciding on the API for protected / private methods with Anyways, appreciate you digging into this! I'll keep my eyes out for subsequent patch releases 👀 |
Awesome - thanks for the quick response! ❤️ |
The regression was inadvertently introduced in this commit [1] and indirectly led to this issue [2]. I've attempted to thoroughly characterise the pre-regression behaviour of Mock#responds_like in a new acceptance test, RespondsLikeTest, in order to reduce the chances of any such regressions occurring in the future. I've also added another acceptance test, ArrayFlattenTest, which reproduced the issue in [2] until the regression was fixed. Investigating this has made me realise that the behaviour of Mock#responds_like in combination with protected & private methods on the responder was not specified in the documentation. I've attempted to reverse engineer the documentation so that it more fully describes the pre-regression behaviour. Note that the CI build for Ruby v1.9 is using an old version of Minitest which doesn't work with the RespondsLikeTest and since support for Ruby v1.9 was dropped in Mocha v2, I don't think it's worth the effort to sort this out so I've just disabled the test for Ruby v1.9. [1]: 365a734 [2]: #580
The regression was inadvertently introduced in this commit [1] and indirectly led to this issue [2]. I've attempted to thoroughly characterise the pre-regression behaviour of Mock#responds_like in a new acceptance test, RespondsLikeTest, in order to reduce the chances of any such regressions occurring in the future. I've also added another acceptance test, ArrayFlattenTest, which reproduced the issue in [2] until the regression was fixed. Investigating this has made me realise that the behaviour of Mock#responds_like in combination with protected & private methods on the responder was not specified in the documentation. I've attempted to reverse engineer the documentation so that it more fully describes the pre-regression behaviour. Note that the CI build for Ruby v1.9 is using an old version of Minitest which doesn't work with the RespondsLikeTest and since support for Ruby v1.9 was dropped in Mocha v2, I don't think it's worth the effort to sort this out so I've just disabled the test for Ruby v1.9. [1]: 365a734 [2]: #580
@adrianna-chang-shopify Sorry for the delay - the fix has been released in v1.15.1, v1.16.1 & v2.0.2. I'm going to close this for now, but let me know if you run into any issues. 🤞 |
We now automatically set a responder on mock object which are used for partial mocks. Having made the change above, I had to set include_all to true for the call to Object#respond_to? in Mock#check_responder_responds_to in order to fix a load of broken tests. The legacy_behaviour_for_array_flatten condition in Mock#check_responder_responds_to is needed to avoid a regression of #580 in Ruby < v2.3. Hopefully this is a small step towards having Configuration.prevent(:stubbing_non_existent_method) check Method#arity and/or Method#parameters (#149) and rationalising Configuration.stubbing_non_existent_method= & Mock#responds_like (#531).
We now automatically set a responder on mock object which are used for partial mocks. Having made the change above, I had to set include_all to true for the call to Object#respond_to? in Mock#check_responder_responds_to in order to fix a load of broken tests. The legacy_behaviour_for_array_flatten condition in Mock#check_responder_responds_to is needed to avoid a regression of #580 in Ruby < v2.3. Hopefully this is a small step towards having Configuration.prevent(:stubbing_non_existent_method) check Method#arity and/or Method#parameters (#149) and rationalising Configuration.stubbing_non_existent_method= & Mock#responds_like (#531).
We now automatically set a responder on mock object which are used for partial mocks. Having made the change above, I had to set include_all to true for the call to Object#respond_to? in Mock#check_responder_responds_to in order to fix a load of broken tests. The legacy_behaviour_for_array_flatten condition in Mock#check_responder_responds_to is needed to avoid a regression of #580 in Ruby < v2.3. Hopefully this is a small step towards having Configuration.prevent(:stubbing_non_existent_method) check Method#arity and/or Method#parameters (#149) and rationalising Configuration.stubbing_non_existent_method= & Mock#responds_like (#531).
Hi!
In trying to upgrade Shopify's primary Rails application from mocha v1.14 to v1.15, a test started to fail unexpectedly. I've set up a repro to mimic what the test was doing (Ruby v3.1.2, but shouldn't matter):
I traced it back to this change. To sum:
Array#flatten
callsrespond_to?(:to_ary, true)
on each element in the array. Withinclude_all
being true, it looks at private methods.respond_to_missing?
delegates to the mock's@responder
, forwarding theinclude_all
argument, sorespond_to?
returns true -- Active Record objects do respond to#to_ary
, but note that this method is private.
respond_to?
returns true here.#to_ary
, however, we check again whether the@responder
responds to the method in question, but this time we don't include private methods.NoMethodError
.This passed prior to v1.15 because we were only forwarding the
include_all
argument when calling@responder.respond_to?(symbol)
if the method's arity was > 1, which was not the case forActiveRecord#to_ary
. Consequently,respond_to?(:to_ary, true)
would return false,Array.flatten
would not attempt to delegate#to_ary
toMock#method_missing
, and we wouldn't raise theNoMethodError.
I'll admit that I'm not sure what the fix is -- is there a way we could have
check_responder_responds_to
handle private methods properly so thatmethod_missing
can kick in properly for a method that has private visibility on the responder? Given that#respond_to_missing?
will return true for private methods wheninclude_all
is true...Thanks! ❤️
The text was updated successfully, but these errors were encountered: