Reputation:
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:
fizzChecker.hasMoreBuzz()
returns false the 1st time it is called (hence the loop never executes)fizzChecker.hasMoreBuzz()
returns false on the 5th time it is calledI'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
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
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