-
-
Notifications
You must be signed in to change notification settings - Fork 358
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
Prevent errors on stub removal #402
Conversation
@@ -60,6 +60,10 @@ def proxy_for(object) | |||
|
|||
alias ensure_registered proxy_for | |||
|
|||
def proxied_instances_of(klass) | |||
proxies.select { |_, proxy| proxy.object.is_a? klass }.map { |_, proxy| proxy } | |||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach is O(N). Instead, we could index the proxies by their class as they are created in a hash, and then do an O(1) lookup here. It would almost certainly be faster for any examples that use lots of mock proxies. It could potentially be slower for examples that use a small number of mock proxies, though...and that might be the more common case. Thoughts?
There's an odd case we need to think carefully about...what should we do when there is an instance of the class that's directly stubbed (e.g. via |
@myronmarston I refactored this to reduce the lookup time and handle unstubbing only any instance methods, take a look? |
Rebased to remove build failure. |
@@ -72,6 +73,10 @@ def unstub(method_name) | |||
raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed" | |||
end | |||
message_chains.remove_stub_chains_for!(method_name) | |||
for proxy in ::RSpec::Mocks.proxies_of(@klass) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use for
here rather than the more commonly seen each
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something I took from a Dave Thomas talk, why do we always use each
for
what are essentially for loops, when for is sometimes clearer, he
attributed it to cargo culting rails.
So sometimes I like to use a for ... in
when I think it might read better.
I'm not overly attached to it though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the consistency of using each
everywhere....and that's what we tend to use.
proxies_by_klass.inject([]) do |klass_proxies, (proxy_klass, object_ids)| | ||
more_proxies = | ||
if proxy_klass.ancestors.include?(klass) || klass.ancestors.include?(proxy_klass) | ||
object_ids.map { |id| proxies[id] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that you need the proxies, and not the object ids, why not store the proxies in the hash rather than ids? Then you don't have to do the proxies[id]
lookup here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proxies are already being stored by id, as this was meant to be a performance tweak over the top for finding them via klass, it made sense to me to stash the smaller ids, rather than duplicating the Proxy objects.
Your approach is interesting in novel :). That said, I do have some concerns:
All that's to say that I think it might be best to go back to this: proxies.each_with_object do |(_, proxy), matches|
matches << proxy if klass === proxy.object
end (Notice that I used |
TL;DR; I dropped it back to the simpler version. Performance wise the other code was going over the classes registered in the mock space, so the lookup was bounded, and there would be less in that hash lookup than in The super/sub class behaviour of |
I get what you're saying, but even though the one thing that uses So thanks for changing this :). Anyhow, I pushed two simple small improvements to some specs (let me know what you think). From here, this needs a changelog entry. It could also benefit from being squashed into one commit: I don't know that the intermediate commits have much value, and 9 commits is a lot for what is a pretty small change. Do you plan to backport this to 2.99 and/or 2.14? |
* access proxies for any instance of klass * remove stubs from "running" instances * pass recorder into chains to register stubs * reduce lookup cost of proxies by klass * record stubs used * remove just the stubs recorded by any instance * switch from for..in to each * allow unstubing of instances that are a sub class * refactor lookup for subclass * Improve doc string. * "local instance" didn't really give me the right * sense for what this was testing. * Improve space spec. - The old spec only checked the number of proxies returned, and didn't actually check that it returned the right ones. - The old spec only tested who were instances of the given class, and did not check instances of subclasses.
Prevent errors on stub removal
Currently the
any_instance
stub functionality doesn't remove stubs from existing instances, this is the start of a fix for #397 but potentially represents a change in behaviour. We'd also need to apply this to re-stubbing I think...Thoughts?