Как проверить вызовы одного и того же метода mock с тем же аргументом, который изменяет состояние между invocations в mockito?
У меня есть следующий код для модульного тестирования:
public void foo() {
Entity entity = //...
persistence.save(entity);
entity.setDate(new Date());
persistence.save(entity);
}
Я хотел бы проверить, что при первом вызове persistence.save
entity.getDate()
возвращает null
.
Поэтому я не могу использовать Mockito.verify(/*...*/)
, потому что в тот момент был вызван метод foo
и entity.setDate(Date)
.
Итак, я думаю, что мне нужно делать проверки вызовов уже в то время, когда происходят вызовы. Как это сделать с помощью Mockito?
Ответы
Ответ 1
Я создал следующую реализацию Answer
:
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);
}
}
В этом ответе фиксируются свойства вызываемых вызовов. Тогда capturedValues
можно использовать для простых утверждений. В реализации используется Java 8 API. Если это не доступно, нужно использовать интерфейс, способный преобразовать InvocationOnMock
в захваченное значение. Использование в тестовом сценарии выглядит следующим образом:
@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();
}
Захват, выполняемый представленной реализацией Answer
, не может быть достигнут с помощью Mockitos ArgumentCaptor
, потому что это используется только после вызова тестируемого метода.
Ответ 2
В моем первоначальном комментарии это был ответ, который я имел в виду.
Класс для издевательства:
class MockedClass{
void save(SomeBean sb){
//doStuff
}
}
Класс, который нам нужен для проверки объекта Date, имеет значение null.
class SomeBean{
Date date;
Date getDate(){return date;}
void setDate(Date date){this.date=date;}
}
Испытуемый класс:
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);
}
}
И тестовый пример:
@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();
}
В первый раз через этот Answer
(термин, который я должен был использовать в своем исходном комментарии), это вызовет исключение и завершит тест, если дата не равна нулю. Второй раз через checkDate
будет false, поэтому проверка не будет выполнена.