btakita / rr

RR (Double Ruby) is a test double framework that features a rich selection of double techniques and a terse syntax.
http://github.com/rr/rr
MIT License
501 stars 58 forks source link

Question: using any_instace_of to stub a method that returns the instance #97

Closed dylanjha closed 11 years ago

dylanjha commented 11 years ago

This returns the class User.

I want to stub the instance method return_me to return the instance

any_instance_of(User) do |u|
  stub(u).return_me { u }
end
mcmire commented 11 years ago

So one thing I want to point out here is that if you pass a block to #any_instance_of, the argument to the block which is yielded is not the User class, it's a DoubleDefinition object. This is the correct usage of #any_instance_of:

any_instance_of(User) do |stub|
  stub.return_me { ... }
end

# or, simpler:
any_instance_of(User) do
  return_me { ... }
end

# or, even simpler:
any_instance_of(User).return_me { ... }

(You can think of #any_instance_of like #stub except it applies to instance methods instead of singleton methods)

With that out of the way -- since stubs merely override methods, in concept you could say something like

any_instance_of(User) do
  return_me { self }
end

However, this won't work either, because the block you provide as the implementation of the method is actually run in the context of the surrounding code. This means self actually points to an instance of your test case (Test::Unit) or describe/context block (RSpec). There may be a possible bug here, I'll have to look into this.

In any case, here's what you want to do instead:

stub.proxy(User).new do |u|
  stub(u).return_me { u }
end

Here we intercept User.new so that every time a new User is instantiated, the new User instance feeds into our callback, where we then override the #return_me method.

mcmire commented 11 years ago

Hey just a quick update -- looks like I was slightly wrong about #any_instance_of. I said that when using #any_instance_of with a block, the argument yielded to the block is a DoubleDefinition, and that is not true. So you do need to write this:

any_instance_of(User) do |user|
  stub(user).return_me { ... }
end

instead of:

any_instance_of(User) do |user_stub|
  user_stub.return_me { ... }
end

(Clearly there is some magic going on there. Forgive me -- I'm still learning new things about this library as I'm studying it)

However my point still stands that you don't have access to the User instance in this form and you need to use stub.proxy. Sorry if that caused any confusion.

dylanjha commented 11 years ago

The first response you had worked for me...

stub.proxy(User).new do |u|
  stub(u).return_me { u }
end

and your next recommendation looks like its not stubbing the method

any_instance_of(User) do |user|
  stub(user).return_me { user }
end
mcmire commented 11 years ago

Hmm, really? The second way doesn't stub the method at all? That part should work regardless of what you return from #return_me...

mcmire commented 11 years ago

I plan on releasing 1.0.5 soon but I want to make sure you aren't having issues with #any_instance_of per se before I do so.