views:

168

answers:

3

I've recently started using mock objects in my tests, but I'm still very inexperienced with them and unsure of how to use them in some cases. At the moment I'm struggling with how to mock inter-method dependencies (calling method A has an effect on the results of method B), and whether it should even be mocked (in the sense of using a mocking framework) at all?

Take for example a Java Iterator? It is easy enough to mock the next() call to return the correct values, but how do I mock hasNext(), which depends on how many times next() has been called? Currently I'm using a List.Iterator as I could find no way to properly mock one.

Does Martin Fowler's distinction between mocks and stubs come into play here? Should I rather write my own IteratorMock?

Also consider the following example. The method to be tested calls mockObject.setX() and later on mockObject.getX(). Is there any way that I can create such a mock (without writing my own) which will allow the returned value of getX to depend on what was passed to setX?

+2  A: 

I would set up my mock to give expected behaviour for both hasNext() and next().

Libraries such as jmock allow you to specify different behaviours for calls to the same method. In concept you would be saying something such as

for the first call to hasNext() return true
for the first call to next() return this record

for the second call to hasNext() return true
for the second call to next() return this record

for the third call to hasNext() return false
djna
Mockito (which I'm using) has similar functionality, but will this still work if the next() and hasNext() calls don't match one-to-one (i.o.w hasNext() is called more times than next()). I know this is probably a bad implementation, but in my cause probably unavoidable.
Zecrates
In the case of jmock you can specify exactly the sequence of expectations, so excessive next() calls would be detected.
djna
Thanks, in that case Mockito will surely allow for the same - I will have a look. I updated my question to include another case which I don't believe can be resolved as easily, please have a look.
Zecrates
+2  A: 

In the general case I would follow the advice of djna above and set up the expectations for both methods. But for the example given I'd likely not use a mock but rather use the APIs to create a collection with the data I want in it and retrieve the iterator from that. (Arrays.asList(...).iterator()).

Edit: Re: set/get What else are you doing with this object? If it is a value object (only gets/sets) I'd not mock it. To much effort for no gain. If it is not then using easyMock:

import org.apache.commons.lang.mutable.MutableInt;
import org.easymock.IAnswer;
import org.junit.Test;
import static org.easymock.EasyMock.*;

public class TestSet {
    @Test
    public void testSetGet() {
     final MutableInt value = new MutableInt();

     GetSetId id = createMock(GetSetId.class);
     id.setID(anyInt());
     expectLastCall().andAnswer(new IAnswer<Object>() {

      @Override
      public Object answer() throws Throwable {
       Object[] arguments = getCurrentArguments();
       value.setValue((Integer) arguments[0]);
       return null;
      }

     });
     expect(id.getID()).andAnswer(new IAnswer<Integer>() {

      @Override
      public Integer answer() throws Throwable {

       return value.toInteger();
      }

     });

     replay(id);
     id.setID((int) (Math.random() * 100.0));
     System.out.println(id.getID());
     verify(id);
    }
}

interface GetSetId {
    public int getID();
    public void setID(int id);
}
mlk
Thanks for the code example. The object isn't just a value object, I just simplified the example.
Zecrates
A: 

If you want a collection, then use one. It's a simple value object that's not worth mocking out. Mocking gets more interesting when you define your own domain types and start to build up a language in the code. Then individual methods do more and protocol becomes less chatty--that's why this kind of complexity is a test smell that suggests that there might be a design flaw.

For the record, jMock (as of today) supports more complex scenarios than Mockito, that's why the initial learning curve is steeper.

Steve Freeman