Thanks for your email and your interest in Mocha.
I think it would be possible to add what you are describing, but it would probably be a little awkward. At the moment when you stub a method using any_instance, you are actually changing the definition of the method on the relevant Ruby class. Naturally this affects *all* classes.
If you wanted to make this selective, I think you'd need to change the method declaration in Mocha::AnyInstanceMethod#define_new_method [1] to include an if/else statement. The condition would provide the selectivity and if it didn't match you'd need to call the original (now hidden) method. If such functionality was added to Mocha, I think I'd want it to use something similar to the Mocha::ParameterMatchers [2] used in the Mocha::Expectation#with rather than the more generic block functionality you've described.
Personally I don't tend to use the any_instance functionality very much. If you feel the need to use it, I think it tends to mean either (a) you are testing the internals of a library which you can't easily change; or (b) the design of your code could be improved to make it more test-able. In scenario (a), I would tend to try to avoid such testing, perhaps by introducing a thin adaptor for the 3rd party library as described in the "Don't mock third-party libraries" section of [2]. In scenario (b), I would prefer to change the design of my code to make it more test-able and avoid the need for any_instance in my tests.
One alternative you may not have considered is to stub the new (or any other class builder) method on the relevant class and then use the existing "with" selectivity to return different instances. These different instances can either be real instances (with relevant methods stubbed) or mock instances. Here's an example [4] which should demonstrate what I mean.
In summary, although I think it would be possible, I'm not totally convinced that such functionality is desirable in Mocha. However, I'm willing to be persuaded. The best way to persuade me is to provide a concrete example where the alternatives are even less desirable.
I hope that helps.
Regards, James.
----
http://jamesmead.org/
[1] https://github.com/floehopper/mocha/blob/master/lib/mocha/any_instance_method.rb#L27
[2] http://mocha.rubyforge.org/classes/Mocha/ParameterMatchers.html
[3] http://www.mockobjects.com/2007/04/test-smell-everything-is-mocked.html
[4] https://gist.github.com/1104229
> --
> You received this message because you are subscribed to the Google Groups "mocha-developer" group.
> To post to this group, send email to mocha-d...@googlegroups.com.
> To unsubscribe from this group, send email to mocha-develop...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/mocha-developer?hl=en.
>
The problem you've detailed in your gist stems from the fact that ActiveRecord < 3.1 doesn't offer an in memory identity map. This particular problem will go away with 3.1, in which the following will pass:
dave = Customer.create! :email_address => 'da...@foo.bar'
assert_same dave, Customer.find_by_email_address 'da...@foo.bar'
That said, how about intercepting the call rather than constraining it?
Customer.any_instance.expects(:deactivate) do
assert_equals 'da...@foo.bar', email_address
end
That would work if the block is executed in the context of the customer object, and would require extending 'expects', but no change to 'any_instance'.
Alternatively:
Customer.any_instance.expects(:deactivate) do |customer|
assert_equals 'da...@foo.bar', customer.email_address
end
Here mocha passes the customer receiving the message to the block. The first one aligns better w/ rspec-mocks, so people who use both libs would have an easier time going back and forth, but the latter feels more intention revealing.
Cheers,
David
Cheers,
David