user1768830
user1768830

Reputation:

Variable output Mockito mocks

I have a class WidgetProcessor that has a dependency on another class, FizzChecker:

public class FizzChecker {
    public boolean hasMoreBuzz() {
        // Sometimes returns true, sometimes returns false.
    }
}

This hasMoreBuzz() method is invoked from inside WidgetProcessor like so:

public class WidgetProcessor {
    public int process() {
        while(fizzChecker.hasMoreBuzz()) {
            // ... process stuff in here
        }
    }
}

I want to write test cases for when:

I'm trying to figure out how to accomplish this with Mockito. So far my best (terrible) attempt:

WidgetProcessor fixture = new WidgetProcessor();
FizzChecker mockFizzChecker = Mockito.mock(FizzChecker.class);

// This works great for the first test case, but what about the 2nd
// where I need it to return: true, true, true, true, false?
Mockito.when(mockFizzChecker).hasMoreBuzz().thenReturn(false);

fixture.setFizzChecker(mockFizzCheck);

fixture.process();

// Assert omitted for brevity

Thanks in advance.

Upvotes: 3

Views: 7327

Answers (2)

Jeff Bowman
Jeff Bowman

Reputation: 95714

You can pass in multiple values to thenReturn, or keep chaining. Successive calls to the stubbed method will return the actions in sequence, repeating the final action for all calls. Examples:

// will return true four times, and then false for all calls afterwards
when(mockFizzChecker.hasMoreBuzz()).thenReturn(true, true, true, true, false);
when(mockFizzChecker.hasMoreBuzz())
    .thenReturn(true)
    .thenReturn(true)
    .thenReturn(true)
    .thenReturn(true)
    .thenReturn(false);
// you can also switch actions like this:
when(someOtherMock.someMethodCall())
    .thenReturn(1, 2)
    .thenThrow(new RuntimeException());

You'll probably want to set them up separately:

public class WidgetProcessorTest {
  private WidgetProcessor processor;
  private FizzChecker mockFizzChecker;

  @Before public void setUp() {
    processor = new WidgetProcessor();
    mockFizzChecker = Mockito.mock(FizzChecker.class);
    processor.setFizzChecker(mockFizzChecker);
  }

  @Test public void neverHasBuzz() {
    when(mockFizzChecker.hasMoreBuzz()).thenReturn(false);
    processor.process();
    // asserts
  }

  @Test public void hasFiveBuzzes() {
    when(mockFizzChecker.hasMoreBuzz())
        .thenReturn(true, true, true, true, false);
    processor.process();
    // asserts
  }
}

Last note: In reality, you may find you need to coordinate multiple calls (such as hasMoreBuzz and getNextBuzz). If it starts to get complicated, and you foresee writing this in a lot of tests, consider skipping Mockito and instead just implementing a FakeFizzChecker.

Upvotes: 7

EJK
EJK

Reputation: 12534

Try using Answers. This will allow you to execute code when the hasMoreBuzz() method is called.

Look at the example in the link provided above. If you create an Answer object, and implement the answer() method to keep a counter, you can take action based on the value of that counter.

Edit: I wrote a quick test program to verify this. Here it is:

package com.ejk;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.mock;

public class SO {
    @Test
    public void testIt() {          
        IFoo mock = mock(IFoo.class);
        MyAnswer myAnswer = new MyAnswer();
        when(mock.doFoo()).then(myAnswer);
        for (int i=1; i<10; i++) {
            System.out.println(i+ ") " + mock.doFoo());
        }
    }
    class MyAnswer implements Answer<Boolean> {
        int counter = 1;
        @Override
        public Boolean answer(InvocationOnMock invocation) throws Throwable {
            return (counter++ == 5) ? Boolean.FALSE : Boolean.TRUE;
        }

    }
    interface IFoo {
        boolean doFoo();
    }
}

Upvotes: 0

Related Questions