Reputation: 289
I have the following situation:
class Worker {
public Integer somework() {
Integer k=0;
Helper h= new Helper();
h.change(k);
return k;
}
}
class Helper {
public void change(Integer k) {
//k = Some calcs
}
}
I'm making unitests for Worker
and obviously I want to mock Helper
class so that his change
method will always put 1 into k
.
My real situation is more complicated but this code represents the issue. Thanks for help.
Upvotes: 22
Views: 15340
Reputation: 367
I think doAnswer is the best method dealing with void method where the method manipulates the given parameters.
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Mock mock = invocation.getMock();
return null;
}})
.when(mock).someMethod();
Basically, after getting the arguments you can do whatever modification you want. There's a blog post explaining it a bit.
Upvotes: 5
Reputation: 424
I have a method with definition like this:
class Template{
public void process(Object rootMap, StringWriter out){
.......
}
}
I will show you how you can change/modify the "out"(StringWriter) reference.
private final Template mocktTemplate = mock(Template.class);
doAnswer(new Answer<StringWriter>() {
public StringWriter answer(InvocationOnMock invocation)
throws Throwable {
Object[] args = invocation.getArguments();
if (args[1] instanceof StringWriter) {
StringWriter stringWriter = (StringWriter) args[1];
stringWriter.write("Email message");
}
return null;
}
}).when(this.mocktTemplate).process(anyObject(),any(StringWriter.class));
Now when you do make the actual call like:
msgBodyTemplate.process(model, msgBodyWriter);
the value of Stringbuffer ref in msgBodyWriter will be "Email message"; irrespective of it earlier value.
Upvotes: 21
Reputation: 691715
I would change the method signature and make it take a Helper
instance as argument. The caller would create the helper and pass it to the somework
method. The test would pass a mock helper.
If this isn't possible, at least call a protected factory method to create the helper, and mock this factory method when testing the somework
method in order to make it return a mock helper:
class Worker {
public Integer somework(){
Integer k=0;
Helper h= createHelper();
h.change(k);
return k;
}
// this method may be mocked when testing somework, to return a mock helper.
protected Helper createHelper() {
return new Helper();
}
}
Upvotes: 3
Reputation: 5971
To @JB Nizet's point, yes, it's good to refactor for testability. Often refactoring to make code more testable leads to code that is better for other reasons - separation of concerns and such. It's not always possible to do. Say it's not your code, or you have some other requirement to leave it alone (because lots of other classes rely on it being the way it is) or whatever.
If I understand what you need to do, I think you can do it with a spy:
Worker workerUnderTest = new Worker();
Worker spiedWorkerUT = spy(workerUnderTest);
Helper mockHelper = mock(Helper.class);
when(spiedWorkerUT.createHelper()).thenReturn(mockHelper);
Integer actual = spiedWorkerUT.someWork();
verify(mockHelper).change(0);
Then use the spiedWorkerUT instead of the workerUnderTest to run your tests.
It's not always possible to avoid instantiating something you want to mock. For that, there is PowerMock.
Helper mockHelper = mock(Helper.class);
whenNew(Helper.class).withNoArguments().thenReturn(mockHelper);
Upvotes: 4