Java Mockito:如何在调用时存根void方法以运行某些代码

Java Mockito:如何在调用时存根void方法以运行某些代码,java,unit-testing,mockito,void,stubbing,Java,Unit Testing,Mockito,Void,Stubbing,我想存根一个存储库类来测试另一个拥有存储库的类(Holder类)。repository接口支持CRUD操作,并且有许多方法,但是我对Holder类的单元测试只需要调用其中的两个。存储库界面: public interface IRepo { public void remove(String... sarr); public void add(String... sarr); //Lots of other methods I don't need now } 我想

我想存根一个存储库类来测试另一个拥有存储库的类(Holder类)。repository接口支持CRUD操作,并且有许多方法,但是我对Holder类的单元测试只需要调用其中的两个。存储库界面:

public interface IRepo {
    public void remove(String... sarr);

    public void add(String... sarr);

    //Lots of other methods I don't need now
}
我想创建一个存储库模拟,它可以存储实例,只为
add
remove
定义逻辑,还可以提供一种在调用add和remove之后检查存储在其中的内容的方法。

如果我这样做:

IRepo repoMock = mock(IRepo.class);
然后我有一个哑对象,它对每个方法都不做任何操作。没关系,现在我只需要定义add和remove的行为

我可以创建一个
集合
并只存根这两个方法来处理集合。然后,我将实例化一个具有IRepo的Holder,注入部分存根的mock,在执行Holder之后,检查集合以验证它包含应该包含的内容

我已使用不推荐使用的方法
stubVoid
,成功地部分存根了一个void方法,如
remove

Set<String> mySet = new HashSet<>();
stubVoid(repoMock).toAnswer(new Answer<Void>() {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Object[] args = invocation.getArguments();
        String[] stringsToDelete = (String[]) args[0];
        mySet.removeAll(Arrays.asList(stringsToDelete));
        return null;
    }
}).on().remove(Matchers.<String>anyVararg());
Set mySet=new HashSet();
stubVoid(repoMock).toAnswer(新答案){
@凌驾
公共Void应答(invocationmock调用)抛出可丢弃的{
对象[]args=invocation.getArguments();
字符串[]stringsToDelete=(字符串[])参数[0];
removeAll(Arrays.asList(stringsToDelete));
返回null;
}
}).on().remove(Matchers.anyVararg());
但是它已经被弃用了,它并不比为IRepo创建一个部分实现好多少。有更好的办法吗

注意:请只回答Java 7,这应该在Android中运行。

假设您有

public class IFace {
    public void yourMethod() {            
    }
}
然后你需要模仿它

    IFace mock = Mockito.mock(IFace.class);
    Mockito.doAnswer(new Answer() {
        @Override
        public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
            //PUT YOUR CODE HERE
            return null;
        }
    }).when(mock).yourMethod();
你可以用

  Mockito.doAnswer(new Answer<Void>() {
        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            //DO SOMETHING
            return null;
        }
    }).when(...).remove(Matchers.<String>anyVararg());

请参阅javadoc for Mockito中的示例

如果在
应答
的实现中确实不喜欢
返回null
,则可以创建自己的应答实现,该实现委托给void方法:

public abstract class DoesSomethingAnswer implements Answer<Void> {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        doSomething(invocation);
        return null;
    }

    protected abstract void doSomething(InvocationOnMock invocation);    
}
公共抽象类DoesMethingAnswer实现了答案{
@凌驾
公共Void应答(invocationmock调用)抛出可丢弃的{
doSomething(调用);
返回null;
}
受保护的抽象void doSomething(invocationmock调用);
}
那么您的测试少了一行:

    Set<String> mySet = new HashSet<>();
    Mockito.doAnswer(new DoesSomethingAnswer() {
        @Override
        protected void doSomething(InvocationOnMock invocation) {
            Object[] args = invocation.getArguments();
            String[] stringsToDelete = (String[]) args[0];
            mySet.removeAll(Arrays.asList(stringsToDelete));
        }
    }).when(repoMock).remove(Matchers.<String> anyVararg());
Set mySet=new HashSet();
Mockito.doAnswer(新的DoesSomethingAnswer(){
@凌驾
受保护的void doSomething(invocationMock调用){
对象[]args=invocation.getArguments();
字符串[]stringsToDelete=(字符串[])参数[0];
removeAll(Arrays.asList(stringsToDelete));
}
}).when(repoMock).remove(Matchers.anyVararg());
或者,如果您只需要调用中的参数:

public abstract class DoesSomethingAnswer implements Answer<Void> {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        doSomething(invocation.getArguments());
        return null;
    }

    protected abstract void doSomething(Object[] args);
}
公共抽象类DoesMethingAnswer实现了答案{
@凌驾
公共Void应答(invocationmock调用)抛出可丢弃的{
doSomething(invocation.getArguments());
返回null;
}
受保护的抽象无效剂量测量(对象[]args);
}

有趣的是,没有
doSomething
接受一个可运行的答案,而不必破解一个无效的答案。我的用例是不是太牵强了,不值得放在Mockito的API中?我怀疑是否有任何库可以满足所有用户的需求。Mockito很容易扩展(见我的答案),几乎可以用于任何目的。顺便说一下,Runnable在您的示例中不起作用,因为您需要调用参数。修复了我在测试中途试图强制更改某些数据的问题,谢谢!
public abstract class DoesSomethingAnswer implements Answer<Void> {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        doSomething(invocation.getArguments());
        return null;
    }

    protected abstract void doSomething(Object[] args);
}