I've come across a similar problem when mocking builders, so I thought I'd experiment to see if there is a nicer way.
As Mr Spoon said, it's probably nicer if you can avoid doing do this in the first place as it is not your code and could be assumed to "just work", but I thought I'd give it a go anyway.
I've come up with a (maybe crude) way of doing it by using "default answers" in Mockito. I'm still deciding if I like it or not.
Here's my builder...
public class MyBuilder {
private StringBuilder my;
public MyBuilder() {
my = new StringBuilder();
}
public MyBuilder name(String name) {
my.append("[name=").append(name).append("]");
return this;
}
public MyBuilder age(String age) {
my.append("[age=").append(age).append("]");
return this;
}
public String create() {
return my.toString();
}
}
(Pretty basic right?)
I got my test to look something like this...
// Create a "BuilderMocker" (any better name suggestions welcome!)
BuilderMocker<MyBuilder> mocker = BuilderMocker.forClass(MyBuilder.class);
// Get the actual mock builder
MyBuilder builder = mocker.build();
// expect this chain of method calls...
mocker.expect().name("[NAME]").age("[AGE]");
// expect this end-of-chain method call...
Mockito.when(builder.create()).thenReturn("[ARGH!]");
Now if I do the following...
System.out.println(builder.name("[NAME]").age("[AGE]").create());
...I expect "[ARGH!]" to be output.
If I altered the last line...
System.out.println(builder.name("[NOT THIS NAME]").age("[AGE]").create());
...then I expect it to break with a NullPointerException.
Here is the actual "BuilderMocker"...
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class BuilderMocker<T> {
private Class<T> clazz;
private T recorder;
private T mock;
// Create a BuilderMocker for the class
public static <T> BuilderMocker<T> forClass(Class<T> clazz) {
return new BuilderMocker<T>(clazz);
}
private BuilderMocker(Class<T> clazz) {
this.clazz = clazz;
this.mock = mock(clazz);
createRecorder();
}
// Sets up the "recorder"
private void createRecorder() {
recorder = mock(clazz, withSettings().defaultAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
// If it is a chained method...
if (invocation.getMethod().getReturnType().equals(clazz)) {
// Set expectation on the "real" mock...
when(invocation.getMethod().invoke(mock, invocation.getArguments())).thenReturn(mock);
return recorder;
}
return null;
}
}));
}
// Use this to "record" the expected method chain
public T expect() {
return recorder;
}
// Use this to get the "real" mock...
public T build() {
return mock;
}
}
Not sure if there is a "built-in" way of doing this in Mockito, but this seems to work.
public String[] search(searchParams..) { SearchResponse response = client.prepareSearch(index).. etc; SearchHit[] hits = response.getHits().getHits(); return hits; }
. Your test should mock out yoursearch
method and return mockString[]
results for different inputs. – Aspergillus