Reputation: 13556
I have the following code to be unit tested:
public void foo() {
Entity entity = //...
persistence.save(entity);
entity.setDate(new Date());
persistence.save(entity);
}
I would like to verify that on the first invocation of persistence.save
entity.getDate()
returns null
.
Therefore I'm unable to use Mockito.verify(/*...*/)
because at that time the method foo
already completed and entity.setDate(Date)
was called.
So I think I need to do verifications of invocations already at the time the invocations happen. How do I do this using Mockito?
Upvotes: 4
Views: 4134
Reputation: 13556
I created the following Answer
implementation:
public class CapturingAnswer<T, R> implements Answer<T> {
private final Function<InvocationOnMock, R> capturingFunction;
private final List<R> capturedValues = new ArrayList<R>();
public CapturingAnswer(final Function<InvocationOnMock, R> capturingFunction) {
super();
this.capturingFunction = capturingFunction;
}
@Override
public T answer(final InvocationOnMock invocation) throws Throwable {
capturedValues.add(capturingFunction.apply(invocation));
return null;
}
public List<R> getCapturedValues() {
return Collections.unmodifiableList(capturedValues);
}
}
This answer captures properties of the invocations being made. The capturedValues
can then be used for simple assertions. The implementation uses Java 8 API. If that is not available one would need to use an interface that is able to convert the InvocationOnMock
to the captured value. The usage in the testcase is like this:
@Test
public void testSomething() {
CapturingAnswer<Void,Date> captureDates = new CapturingAnswer<>(this::getEntityDate)
Mockito.doAnswer(captureDates).when(persistence).save(Mockito.any(Entity.class));
service.foo();
Assert.assertNull(captureDates.getCapturedValues().get(0));
}
private Date getEntityDate(InvocationOnMock invocation) {
Entity entity = (Entity)invocation.getArguments()[0];
return entity.getDate();
}
The capturing that is done by the presented Answer
implementation can't be achieved with Mockitos ArgumentCaptor
because this is only used after the invocation of the method under test.
Upvotes: 11
Reputation: 3915
In my original comment, this was the answer I had in mind.
The class to be mocked:
class MockedClass{
void save(SomeBean sb){
//doStuff
}
}
The class we'll need to verify the Date object is null.
class SomeBean{
Date date;
Date getDate(){return date;}
void setDate(Date date){this.date=date;}
}
The class under test:
class TestClass{
MockedClass mc;
TestClass(MockedClass mc){this.mc = mc;}
void doWork(){
SomeBean sb = new SomeBean();
mc.save(sb);
sb.setDate(new Date());
mc.save(sb);
}
}
And the test case:
@Test
public void testAnswer(){
MockedClass mc = Mockito.mock(MockedClass.class);
Mockito.doAnswer(new Answer<Void>(){
boolean checkDate = true;
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
SomeBean sb = (SomeBean) invocation.getArguments()[0];
if(checkDate && sb.getDate() != null){
throw new NullPointerException(); //Or a more meaningful exception
}
checkDate = false;
return null;
}}).when(mc).save(Mockito.any(SomeBean.class));;
TestClass tc = new TestClass(mc);
tc.doWork();
}
The first time through this Answer
(The term I should have used in my original comment), this will throw an exception and fail the test case if date is not null. The second time through, checkDate
will be false, so the check will not be performed.
Upvotes: 1