Zach Dennis wrote:
I'm top posting... I wish I could inline post, but you provided a lot
of generalizations for how you think things are working on your code,
but you don't actually post concrete code (the should_receive and
should_not_receive case you mentioned that wasn't acting like you'd
expect). Perhaps this will help:

* Mocks are not ordered by default.

Yeah that's consistent with what I'm seeing. I mentioned ordering only because it was one of the things that came up in my mind as I was searching for a way to understand the different behaviors I was seeing.

* If you want ordered message expectations on a mock you have to
explicitly tell them to be ordered.

Really, you can do that? I'm curious about how.

* You cannot enforce ordering across mocks.

So without explicitly ordering, message expectations can be fulfilled
in any order. For example the below will pass even though the
expectation for 1 and 3 does not match the order in when the bar
method is called:

    file = Object.new
    file.should_receive(:bar).with("1")
    file.should_receive(:bar).with("3")

    file.bar "3"
    file.bar "1"

Ok got that.

Once a message expectation is fulfilled, if the object receives
another message matching that expectation it will force it to fail.
For example, the following would fail even though there are two
expectations for "bar" to be called with "1", which match the two
calls to file.bar:

    file = Object.new
    file.should_receive(:bar).with("1")
    file.should_receive(:bar).with("1")

    file.bar "1"
    file.bar "1"

This one kind of surprises me, but I understand your point.

Using should_not_receive and should_receive on the same message gets
tricky, because you can trick yourself into thinking the thing is
passing when it shouldn't be. For example the below will fail because
even though you call file.bar with "2", that matches the expectation
that file.bar should not be called with "3":

    file = Object.new
    file.should_receive(:bar).with("1")
    file.should_not_receive(:bar).with("3")

    file.bar "1"
    file.bar "2"

Did you mean to say that will *not* fail since "2" will match "not 3"? If so that's really good to know. I had imagined that should_not_receive was purely a statement about something that doesn't happen, not about something different happening. That may explain what I had thought was anomalous behavior. I'll have to take another look at that.


Hopefully this helps clarify some things for you about rspec's mocks.

However...

object.should_receive :method => :method_name, :with_each_of => [arg1,
arg2,... argN]

I really don't like this. This is hard to read and can lead to
something that is very convoluted. Can you share the actual code and
spec you're having issues with so we can try to provide concrete help
back to you?
Steve's comments helped me understand why this approach isn't necessary.

Mark.



Zach


On Sat, Oct 18, 2008 at 9:49 PM, Mark Thomson <[EMAIL PROTECTED]> wrote:
Stephen Eley wrote:
On Fri, Oct 17, 2008 at 2:55 PM, Mark Thomson <[EMAIL PROTECTED]>
wrote:

and then check that the expected messages are being received -
file.should_receive(:puts).with("a string").once
file.should_receive(:puts).with("another string").once

Here's what I'm puzzled about. If I don't include the expectation for the
first string in the spec, the spec will fail the expectation for the
second
string. It seems as if "should_receive" is queuing up the messages that
come
into the file object and when it tests an expectation it just looks at
the
next one in line. If it doesn't match then the expectation will fail.

That sounds right to me.  You declared 'file' as a mock.  Mocks are
bratty little children that treat it as an error and throw a tantrum
if you don't give them everything they expect, no more nor less.  (As
contrasted to stubs, which are couch potatoes that will respond if you
call them but don't complain if you don't.)

So when you create a mock, you need to be very thorough about it.
Every message has to be accounted for somehow.  If it gets more
messages than you tell it, or different messages, it'll error.  If it
doesn't get enough messages, it'll error.  This is correct behavior.

Thanks for the explanation Stephen. However, if that is the intention, I'm
puzzled by something else - as I said the spec fails if I don't include an
expectation for each output message. However it turns out that that's
actually not always true. What I've observed is that it behaves differently
if I include a "should_not_receive('...')" expectation somewhere in the
spec. In that case it seems that I can have as many "file.puts()" in the
component being tested as I like without specifying expectations for them,
and they pass just fine. In fact I did have such a situation in my initial
spec and I think that's what led me to my mistaken understanding of how
should_receive is meant to work. But I'm struggling to understand what the
rationale is that explains both of these cases.

That aside, I also can't help questioning the way the "should_receive"
expectation is expressed. Maybe specifying every message sent to the mock is
absolutely the right way to test the component. But in view of the general
philosophy of expressing expectations in a way that reflects what they
actually mean, in my mind this doesn't quite hit the mark. If you say
"should receive", the way I read that is that if the object /does /receive
what you specify then it should pass. But that's clearly not what happens.
Nor is it  an expectation on what will be received next. If that were the
case you might call the method "should_next_receive". However, in fact, as
long as all messages are accounted for, you can reorder the individual
"should_receive" expectations any way you like and the spec will still pass.

In fact "should_receive" does not appear to be an expectation on a single
message at all (even if you say "should_receive().once", and leaving aside
the exception with "should_not_receive" I noted above). I think a better way
to think about this is that the total set of "should_receive" calls are
_together_ an expectation on the totality of messages received by the
object. In view of this, I wonder if a better way to formulate this test
might be to say something like -

object.should_receive :method => :method_name, :with_each_of => [arg1,
arg2,... argN]

where arg1, arg2 etc represent the parameters for each individual call to
:method_name. i.e. declare the whole of what we _really_ expect the object
to receive in a single call to should_receive.

You could take this one step further and declare all of the required calls
to any number of methods on the object in a single expectation, by making
the argument to should_receive an array -

object.should_receive [{:method => :method1, :with_each_of => [...]},
{:method => :method2, :with_each_of => [...]}, ...]

Yeah it gets a little wordy, but if I'm understanding the behavior
correctly, this is what we are actually trying to test.

Does this make sense?

Mark.
_______________________________________________
rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users




_______________________________________________
rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to