-
-
Notifications
You must be signed in to change notification settings - Fork 161
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
NoMethodError when stubbing any instance of class whose respond_to? depends on object state #435
Comments
I might have got the wrong end of the stick, but I think I did some work on something similar in the past. Rather than calling allocate it's possible to call the constructor with the relevant number of required arguments (determined from class RespondToEverything
def method_missing(symbol, *args)
self.class.new
end
def respond_to?(string, include_all=false)
true
end
end This means it is possible to call the constructor rather than allocate. However, if the object genuinely Maybe it's too much of an edge case. I've never been keen on using the any instance approach anyway except in legacy code where I can't easily change the code I want to test. |
From what I can tell, calling the constructor with an object that RespondsToEverything will help matters, because it won't allow/enable An alternative that I haven't properly thought through would be to allow However, I'm inclined to agree with the last paragraph of your comments. Too much of an edge case, and not recommended anyway. Not sure it's worth adding complexity to the code. I'd much rather add a warning to the documentation to inform users of the current behavior and the reasoning/limitation driving it. Clients who can't live with that always have the option to do something like:
|
I assume you mean "calling the constructor with an object that RespondsToEverything won't help matters"...? Anyway, I understand the problem better now. Thanks!
Hmm. That sounds awkward. I'm not convinced!
That seems sensible. I like the idea of adding something like that to the documentation. One last thought. I haven't thought through whether this would be possible to implement, but I wonder if we could take an approach along the lines of Foo.any_instance.responds_like(Foo.new(:arg1, :arg2)).expects(:bar) or Foo.any_instance.responds_to(:bar).expects(:bar) What do you think? |
Both of those options seem sensible. The first one has the downside of needing to know and being able to construct a Foo (which may be complicated). So, I'd prefer the second one. It's almost like type declaration, except that instead of a compiler, you're providing mocha some 'type' information to allow the stub to pass the However, amongst all of them, I'd prefer just skipping the BTW, you probably realize this already, but I should still clarify that the workaround of stubbing |
Just want to report that a similar issue was introduced by a Rails 6 change: rails/rails#33227. After this change,
The workaround I can think of currently is to call
|
@wpliao1989 Thanks for reporting. 👍 |
We just turned on We have a helper called
and changing it to: def mock_like_instance_of(klass, mock_name: klass.name, **extra_mock_args)
+ klass.define_attribute_methods if klass < ActiveRecord::Base
mock(mock_name, extra_mock_args).tap { |m| m.responds_like_instance_of(klass) }
end helped. It seems like we could introduce a similar change to mocha's own implementation of def responds_like_instance_of(responder_class)
+ responder_class.define_attribute_methods if defined? ActiveRecord::Base && responder_class < ActiveRecord::Base
responds_like(responder_class.allocate)
end Might work. Maybe that change should be in a special mocha/rails file to be included in rails apps only? |
@h-lame Thanks for reporting this and for suggesting a fix. I haven't had as much time as I would like to stay on top of this thread. Am I right in thinking that your fix wouldn't address the problem @wpliao1989 is seeing. To fix that we'd need to change the code in |
If I get the chance I'll try to investigate whether RSpec is doing anything about this, but that might not be for a day or two, so anyone on this thread should feel free to investigate! 😉 |
@floehopper - you're right. Seems like anywhere that mocha calls
I don't know that mocha can do much about the former, feels like users should be driven towards using bare FWIW: this seems to be what |
This follows on from #432.
If either
stubbing_non_existent_method
orstubbing_non_public_method
configuration options are set to:warn
or:prevent
(i.e. not the default value of:allow
) and you try to stub a method on any instance of a class like the one defined below using something likeFoo.any_instance.stubs(:foo)
, then I think you will see theNoMethodError
mentioned in #432.The text was updated successfully, but these errors were encountered: