Mockito:如何在调用时将 void 方法存根到 运行 一些代码

Mockito: how to stub void methods to run some code when called

我想存根存储库 class 以测试另一个具有存储库的 class(持有人 class)。 repository接口支持CRUD操作,方法很多,但是我在Holderclass上的单元测试只需要调用其中两个即可。存储库界面:

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

    public void add(String... sarr);

    //Lots of other methods I don't need now
}

我想创建一个可以存储实例的存储库模拟,仅为 addremove 定义逻辑,并提供一种检查存储在其中的方法在调用添加和删除之后。

如果我这样做:

IRepo repoMock = mock(IRepo.class);

然后我有一个哑对象,它对每个方法都不做任何事情。没关系,现在我只需要定义添加和删除的行为。

我可以创建一个 Set<String> 并仅存根这两种方法以在集合上工作。然后我将实例化一个具有 IRepo 的持有人,注入部分存根的模拟,并在行使持有人之后,检查集合以验证它包含它应该包含的内容。

我已经设法使用已弃用的方法 stubVoid:

部分存根了 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());

但已被弃用,它并不比为 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:

Use doAnswer() when you want to stub a void method with generic Answer.

Stubbing voids requires different approach from Mockito.when(Object) because the compiler does not like void methods inside brackets...

Example:

    doAnswer(new Answer() {
    public Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        Mock mock = invocation.getMock();
        return null;
    }}).when(mock).someMethod();

请参阅 Mockito 的 javadoc 中的示例

如果您真的不喜欢 return nullAnswer 的实现中,您可以创建自己的 Answer 实现,委托给 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);    
}

那么你的测试就少了一行:

    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());

或者,如果您只需要调用中的参数:

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);
}

除了 你还可以使用 lambda:

Mockito.doAnswer(
        invocation -> {
            // DO SOMETHING
            return null;
        }
).when(repoMock).remove(Matchers.<String>anyVararg());