[rspec-users] Test doubles: expect "x" and don't care about anything else
matt at mattwynne.net
Sun Jun 28 17:04:39 EDT 2009
On 28 Jun 2009, at 13:07, Wincent Colaiuta wrote:
> I've had one of my recurring doubts about test doubles come up again.
> The full post is here but I'll abbreviate the content in this
> message in any case:
> Basically, in one of my controller specs I wanted to verify that the
> following line was being called and doing the right thing:
> @comment = Comment.find params[:id]
> I had a mock for this set up, but it broke when unrelated code in
> the model was modified (a complex callback which itself called
I'd like to know more about how this happened. How did the model
object's behaviour leak into the controller spec?
> The problems were as follows:
> - A mock was more than I really needed, as I didn't want to go
> through the complication of returning a substitute object.
> - The expectation set on the mock was too strict, because the other
> message send to Comment.find, the one I didn't care about, was
> triggering a failure.
> - A proxy would suffice, because all I really wanted to confirm was
> that the "find" message was sent, without actually interfering with
> the returned object.
> - I basically wanted to set an expectation "that this class will
> receive this message with these params", but the frameworks didn't
> allow me to do that because in reality you can only assert "that
> this class will receive this message with these params _and not
> receive that message with any other params at any time_"
> In my blog post I detailed the possible options for avoiding the
> problem, and the easiest ended up being: forget mocks and proxies
> entirely and instead test the side-effect (that the expected object
> ends up getting assigned to the "@comment" instance variable).
> So the workaround worked, but RSpec's own mock framework, and from
> what I can tell, the alternatives such as Mocha, RR et al, wouldn't
> really let me make the kind of assertion that I wanted to make: ie.
> "confirm this message gets sent at some point, but don't modify the
> behaviour at all, and don't interfere with or worry about any other
> messages that get sent to the object, including messages sent to the
> method that I'm setting the expectation on".
> In my ideal test-double framework, I'd like to really assert two
> things about the line of code in question:
> 1. That Comment.find gets called with a specific param at some
> point in time.
> 2. That the @comment instance variable gets the expected value
> assigned to it.
So why not use
> I literally don't care about what other messages get sent to
> Comment, nor about other places in the code where Comment.find might
> get called with other parameters, and in any case I don't want to
> actually modify the behaviour or substitute my own return values.
> But it seems I can't do this with existing test double frameworks,
> and it makes it hard to write minimal specs with one expectation and
> as few test doubles as possible (ideally zero or one) per "it" block.
> Ideally I'd want to write something like:
> it 'should find the comment' do
> it 'should assign to the @comment instance variable' do
> assigns[:comment].should == @comment
> Note that with the "proxy" syntax above I'm trying to say:
> - I expect this message and these params to be sent at some point
> - I don't care if other messages are sent
> - I don't even care if the "find" method is also called with
> different params
> - I don't care about the order of the messages
> - I don't want to interfere with or substitute the return value
> I don't know whether the syntax is adequate, or whether some keyword
> other than "proxy" would be required. Another alternative I thought
> of was:
> it 'should find the comment' do
> Comment.should have_received.find(@comment.id.to_s)
> Or similar... Basically saying that I want the double framework to
> spy (proxy _and_ record) all messages to the specified receiver, and
> that afterwards I'm going to retrospectively check that among the
> recorded messages is the one I'm looking for.
> What do other people think?
> - is what I'm wanting to do a reasonable approach?
> - are there any test double frameworks out there which would allow
> me to work in this way?
More information about the rspec-users