Heck, it can not hurt to have the alternate. But I'd have little
feedback on the total abstraction of a core with external alternate
external APIs.
- Ken
> --~--~---------~--~----~------------~-------~--~----~
> 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
> -~----------~----~----~----~------~----~------~--~---
>
Joe just wrote a blog post about test spies:
http://giantrobots.thoughtbot.com/2009/8/6/spy-vs-spy
We've been using them with great success and enjoyment at thoughtbot
for months. We'd love to see spies make it into mocha so we're not
working off a non-standard branch.
I'd also add that we've found test spies to be a good way to teach
Rails students mocking and stubbing in our training classes.
We harp on the Four-Phase Test (setup, exercise, verification,
teardown) throughout the classes, so when it comes to mocha, it's a
more natural mental transition to use assert_received like a normal
verification. The necessary stubbing in the setup is still confusing
for students.
+1 test spies in mocha!
Cheers, James.
I'll try and take another look at this in the next few days.
Cheers, James.
Hi Rob,
I wrote up my thoughts about Test Spies in a blog article [1]. I
thought I'd posted a link to that article on this list, but it looks
like I didn't - sorry. Anyway, the upshot is that I need more
convincing to add Test Spies into Mocha. Do you have some compelling
examples we could see?
Cheers, James.
[1] http://blog.floehopper.org/articles/2009/09/14/mocha-test-spies
Thanks for your response. I wanted to let you know I have seen it, but
I'm not sure when I'll have time to look at it in the detail that I
want. I do however have one quick comment regarding this paragraph :-
2009/10/21 Joe Ferris <joe.r....@gmail.com>:
> I agree with the general rule of thumb, but that only works out for
> extremely simple interfaces. Post.find must be called with a specific
> value in order for the behavior to be at all correct - Post.find(:all)
> or Post.find('other_id') would be a total failure, so only stubbing it
> out in this case is inadequate.
Are you aware that you can restrict a stubbed method using a parameter
matcher in the same way as you can restrict an expected method? In the
case you describe above, I would prefer to use
Post.stubs(:find).with('correct_id').returns(post) and then (if deemed
important) assert later in the test that the correct instance of Post
has been found. If Post.find is called with the wrong parameter (e.g.
'other_id'), the stubbed method will return nil and the later
assertion will fail. Since the call to Post.find does not change the
state of an object, I think it's overkill to "expect" the call and
leads to a brittle test and potentially confusing failures. What do
you think?
As always I think discussions like this are better when focussed on
concrete examples. Do you have any more examples of tests that are
improved by the use of Test Spies?
Cheers, James.
Post.find
method has been called – where else would the controller have got the instance of Post
from?"Hi Curran,
Thanks for your questions. I don't claim to have all the answers, but
here are a few ideas :-
One way of relaxing the constraint on the database ID needing to be a
String would be to define your own Mocha parameter matcher for
matching integer equivalents. I've knocked up a quick (untested)
version [1]. This would allow you to do the following :-
User.stubs(:find).with(integer_equivalent(@user.id)).returns(@user)
Regarding the call to User.find made within
current_user.authenticated?, is there any reason this doesn't call
User.find with a database ID too? If you could change it so it did,
you could have the following two stubs :-
User.stubs(:find).with(integer_equivalent(@user.id)).returns(@user)
User.stubs(:find).with(integer_equivalent(users(:quentin).id)).returns(users(:quentin).id)
Another possibility would be to extract a method from
current_user.authenticated? ideally with a method name (better than
mine) expressing why it needs to be different from User.find(id) :-
class User
def self.special_find_by_id(id)
find(:first, :conditions => { :id => id })
end
end
Then you could have the following two stubs :-
User.stubs(:find).with(integer_equivalent(@user.id)).returns(@user)
User.stubs(:special_find_by_id).with(integer_equivalent(users(:quentin).id)).returns(users(:quentin).id)
If you can't change the current_user.authenticated? code, you could
extract the expected parameters of the User.find method into a private
explanatory method within your test (or maybe a module shared amongst
tests):-
def current_user_params_for(user)
[ :first, :conditions => { :id => users(:quentin).id } ]
end
User.stubs(:find).with(*current_user_params_for(users(:quentin))).returns(users(:quentin))
If you want this to be a more relaxed constraint, you could build a
combinatorial parameter matcher along the following lines :-
def current_user_matching(user)
[ all_of(includes(:first), includes(has_entry(:conditions =>
has_entry(:id => user.id)))) ]
end
User.stubs(:find).with(*current_user_matching(users(:quentin))).returns(users(:quentin))
Or you could even build another specialist custom parameter matcher.
I'll leave that as an exercise for the reader ;-)
In general, I think that custom parameter matchers are an under-used
aspect of Mocha. I'd be very open to adding new matchers to Mocha if
they are thought to be generally useful.
Something else to keep in mind is that if it's becoming difficult to
setup all the expectations you need in a test, it might be time to
think about simplifying the design. Extracting the
User.special_find_by_id is one (not very good) example of this.
Another option is that you might be able to stub a method higher up
the call chain (e.g. on the controller) to avoid the call to
current_user.authenticated? altogether. Depending on what you are
trying to test, this may also make your test more expressive and
focussed.
I hope some of the above is of some use. Please note that I haven't
had a chance to test any of the example code, so please take it with a
pinch of salt!
Cheers, James.
Another option is that you might be able to stub a method higher up
the call chain (e.g. on the controller) to avoid the call to
current_user.authenticated? altogether. Depending on what you are
trying to test, this may also make your test more expressive and
focussed.