Re: mock assertions on block parameters

On 7 June 2010 15:25, Matt W. [email protected] wrote:

Have you seen and_yield? I can’t quite get my head around what you’re
trying

to do, but it might help anyway

Thank you, I have. My understanding is that #and_yield has much the same
use
as #and_return (in its non-bastardized-by-me-form). That is, you specify
it
in order to make the mocked-out collaborator behave enough like the real
collaborator that the object-under-test can work properly.

I’ll try to explain what I’m aiming for more comprehensibly.

If I my object-under-test calls a method with a parameter, I can make
assertions against the values passed. The simplest is equality:

foo.should_receive(:bar).with(6)

The corresponding code in the object-under-test is

foo.bar(6)

and the real code for the collaborator is something like:

class Foo
def bar(x)
// …
end
end

Now the collaborator in my case doesn’t take a parameter, it takes a
block:

class Foo2
def bar2(&block)
// …
end
end

and the object-under-test passes in a block which is called later for
its
return value:

foo2.bar2 { 6 }

(The real block doesn’t return a constant, obviously. It returns
something
which needs to be evaluated lazily because its dependencies don’t exist
yet.)

I want to test that the block passed in is the block that I expect.
Something like (in an imaginary world):

foo2.should_receive(:foo2).with(block(yielding(6)))

(In fact, in my case I can’t just assert equality on the return value
from
the block. I’m actually going to test its type.)

The corresponding test using my obscene #and_return hack looks like
this:

foo2.should_receive(:foo2).and_return { |block| block.call.should ==6
}

But this doesn’t scale to multiple calls to #foo2 in the object under
test
because RSpec (understandably) matches its expectations against
invocations
based on method name and argument matchers.

-Ben

On 7 Jun 2010, at 15:56, Ben Butler-Cole wrote:

foo.should_receive(:bar).with(6)
end
and the object-under-test passes in a block which is called later for its return value:

The corresponding test using my obscene #and_return hack looks like this:

foo2.should_receive(:foo2).and_return { |block| block.call.should ==6 }

But this doesn’t scale to multiple calls to #foo2 in the object under test because RSpec (understandably) matches its expectations against invocations based on method name and argument matchers.

I’m still not quite clear what you’re trying to achieve. Do you want to
assert that a specific block is passed to the collaborator, or do you
want to assert that any old block with a particular behaviour is passed
to the collaborator?

If it’s the former, you can test it by creating a test double for the
block, and asserting that it’s passed to the collaborator, like this:

Does that help?

On 7 June 2010 16:34, Matt W. [email protected] wrote:

I’m still not quite clear what you’re trying to achieve. Do you want to

assert that a specific block is passed to the collaborator, or do you want
to assert that any old block with a particular behaviour is passed to the
collaborator?

If it’s the former, you can test it by creating a test double for the
block, and asserting that it’s passed to the collaborator

The later. The block gets created by the code under test, so I can’t
create
a double for it.

-Ben

On 7 Jun 2010, at 16:52, Ben Butler-Cole wrote:

On 7 June 2010 16:34, Matt W. [email protected] wrote:

I’m still not quite clear what you’re trying to achieve. Do you want to assert that a specific block is passed to the collaborator, or do you want to assert that any old block with a particular behaviour is passed to the collaborator?

If it’s the former, you can test it by creating a test double for the block, and asserting that it’s passed to the collaborator

The later. The block gets created by the code under test, so I can’t create a double for it.

Aha OK. I don’t know how to do that using RSpec’s mocking framework. I’d
probably just build my own test double for the collaborator, giving it
some simple behaviour to test the block. Something like this:

There might be a way to do this using custom matchers[1] but I don’t
know how to do it myself. Hopefully someone else will chime in if there
is.

cheers,
Matt

[1]http://wiki.github.com/dchelimsky/rspec/custom-matchers