Java 如何使用在mockito中更改调用之间状态的相同参数验证相同mock方法的调用?

Java 如何使用在mockito中更改调用之间状态的相同参数验证相同mock方法的调用?,java,unit-testing,mockito,Java,Unit Testing,Mockito,我有以下代码需要进行单元测试: public void foo() { Entity entity = //... persistence.save(entity); entity.setDate(new Date()); persistence.save(entity); } 我想验证一下,在第一次调用persistence.saveentity.getDate()时,返回null 因此,我无法使用Mockito.verify(/*…*/),因为当时方法foo

我有以下代码需要进行单元测试:

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实现这一点?

我创建了以下
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);
    }

}

用Mockitos
ArgumentCaptor
无法实现所呈现的
Answer
实现所完成的捕获,因为这仅在调用测试中的方法之后使用。

在我最初的评论中,这是我想到的答案

要模拟的类:

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();
}
@测试
公共无效测试答案(){
MockedClass mc=Mockito.mock(MockedClass.class);
Mockito.doAnswer(新答案(){
布尔值checkDate=true;
@凌驾
公共Void应答(invocationmock调用)抛出可丢弃的{
SomeBean sb=(SomeBean)invocation.getArguments()[0];
if(checkDate&&sb.getDate()!=null){
抛出新的NullPointerException();//或更有意义的异常
}
checkDate=false;
返回null;
}}).when(mc).save(Mockito.any(SomeBean.class));;
TestClass tc=新的TestClass(mc);
tc.doWork();
}

第一次通过这个
答案
(我应该在我的原始评论中使用这个术语),如果date不为null,这将抛出一个异常并使测试用例失败。第二次到,
checkDate
将为false,因此将不执行检查。

取消该项。。。没有注意到这是实现:-)澄清一下,听起来您想在第一次保存之后基本上检查日期是否为空?听起来,在第一次保存之后,您基本上是在注入一行代码。我不确定这是否可能。如果正在模拟的是
persistence
,则如果save方法为null,则可以模拟该方法失败。@ChristopherSchneider
persistence
是正在模拟的对象。但我不明白你的最后一句话。如果我不清楚,对不起。看看我的评论,我可以看出我没有很好地解释,但看起来你已经明白了。更具体地说,我的意思是,当您调用
save()
时,这是一个模拟方法。如果第一次调用date时不为null,则会失败(例如,可以抛出RuntimeException)。第二次通过时,应设置为不失败。我将发布一个答案。当使用引发异常的自定义答案实现时,请确保被测试单元不会处理此类异常。
@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();
}