[mocha-developer] counter-intuitive behaveour when passing a proc to Mocha::Expectation#returns

Tomas Pospisek tpo2 at sourcepole.ch
Thu Jan 11 09:23:27 EST 2007


Quoting James Mead <jamesmead44 at gmail.com>:

> On 11/01/07, Tomas Pospisek <tpo2 at sourcepole.ch> wrote:
> >
> > Let's say that I have a procedure that:
> >
> > * gets the number of bytes to write into some file
> > * and returns the number of successfully written bytes
> >
> >    def save( text, len )
> >
> > This procedure is being used all over the place especially inside the
> > method
> > "process" which I want to test. Thus in order not to polute my filesystem
> > I stub
> > the "save" procedure. But of course in order for my "process" method I
> > need the
> > "save" procedure to return the correct number of bytes. Thus naively I'd
> > do
> > something like this:
> >
> >    stubs( :save ). returns( lambda { eval "len" } )
> >
> > Unfortunately this doesn't work, since the "stubs" method doesn't at all
> > take
> > parameter names into account. As far as I can see, this is the place where
> > the
> > actual method replacement is being done:
> >
> >    def define_new_method
> >      stubbee.metaclass.class_eval "def #{method}(*args, &block)
> >      mocha.method_missing(:#{method}, *args, &block); end"
> >    end
> >
> > and it just passes all the arguments as an array. I'm not suggesting the
> > fix for
> > the problem at hand is trivial - I can't see at a glance how one can
> > determine
> > the signature of some method wrt to its parameter*names*, however from the
> > Mocha-user's aka tester's perspective Mocha's behaveour is quite
> > confusing.
> >
> > *t, exploring Mocha
>
>
> Currently Mocha does not allow the Proc in returns() to have access to the
> parameters passed into the stubbed method. Although it would be technically
> possible, I don't think this is a good idea as it would allow you to add
> behaviour to the stub. Stubs are all about canned responses (see
>
http://www.martinfowler.com/articles/mocksArentStubs.html#TheDifferenceBetweenMocksAndStubs).

The example given for a stub in the referred essay is this:

  public class MailServiceStub implements MailService {
    private List<Message> messages = new ArrayList<Message>();
    public void send (Message msg) {
      messages.add(msg);
    }
    public int numberSent() {
      return messages.size();
    }
  }

Mocha won't allow you to do this, since you'd need to have access to "Message
msg" when stubbing the "send" method above, which isn't the case as you note.

Thus we are not able to save the message for further verification - f.ex. if the
calling unit, which we want to test is actually passing the message on to the
MailServiceStub or whether it's eating it.

Now arguably that already crosses the line to a mock, since what the above does
in this case is observe behaveour, as defined by Martin Fowler in the above
article. Which raises the question, whether there's really a marked and
meaningfull difference between the concepts or whether it's "only" the original
author that doesn't have an absolutely tight grasp on where the frontier between
stubs and mocks (and fake objects) really lies?

> What you are talking about sounds more like a Fake Object.
>
> If what you want to do is stub the 'save' method for your tests so that it
> appears that files always get saved successfully, but nothing gets written
> to the file-system - I'd be more inclined to define a fake version of the
> class which have the desired implementation. This is what the 'test/mocks'
> directory allows you to do in Rails. Alternatively you might be able to use
> dependency injection to inject the fake version from your tests.
>
> If you want a more detailed response, please post an example test and the
> code under test.

I'm fine with your response, thanks a lot. I do however suggest to either
document the fact that stubs don't have access to parameters or uuuugh ... to
enable (implement) the access ;-) ...

Actually, not being able to access the parameters led me to rethink and rewrite
the test and implement it more cleanly by using

  mock = stub( :method )
  mock.with() { |method_params, ...| check( method_params ) }

so the side-effect of stubs not allowing parameter access is actually
positive... ;-)

Thanks again,
*t



----------------------------------------------------------------
This message was sent using IMP, the Internet Messaging Program.


More information about the mocha-developer mailing list