简体   繁体   English

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

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

I want to stub a repository class to test another class (Holder class) that has a repository.我想存根一个存储库类来测试另一个具有存储库的类(Holder 类)。 The repository interface supports CRUD operations, and has many methods, but my unit test on the Holder class only needs to call two of them.存储库接口支持CRUD操作,方法很多,但是我对Holder类的单元测试只需要调用其中的两个。 The repository interface:存储库界面:

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

    public void add(String... sarr);

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

I want to create a repository mock that can store instances, define logic for add and remove only, and also provide a means of checking what is stored on it after calling add and remove.我想创建一个可以存储实例的存储库模拟,定义仅用于addremove逻辑,并提供一种在调用添加和删除后检查存储在其中的内容的方法。

If I do:如果我做:

IRepo repoMock = mock(IRepo.class);

Then I have a dumb object that does nothing on each method.然后我有一个愚蠢的对象,它对每种方法都不做任何事情。 That's OK, now I just need to define behaviour for add and remove.没关系,现在我只需要定义添加和删除的行为。

I could create a Set<String> and stub only those two methods to work on the set.我可以创建一个Set<String>并仅存根这两种方法来处理该集合。 Then I'd instantiate a Holder that has an IRepo, inject the partially stubbed mock, and after exercising the holder, check the set to verify it contains what it should.然后,我会实例化一个具有 IRepo 的 Holder,注入部分存根的模拟,并在运行该持有者后,检查该集合以验证它包含应有的内容。

I've managed to partially stub a void method like remove using the deprecated method stubVoid :我已经设法使用已弃用的方法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());

But is deprecated, and it is not much better than creating a partial implementation for IRepo.但已弃用,并不比为 IRepo 创建部分实现好多少。 Is there a better way?有没有更好的办法?

NOTE: Java 7 answers only please, this should run in Android.注意:请只回答 Java 7,这应该在 Android 中运行。

You can use 您可以使用

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

From the Javadoc: 来自Javadoc:

Use doAnswer() when you want to stub a void method with generic Answer. 如果要使用通用答案存根void方法,请使用doAnswer()。

Stubbing voids requires different approach from Mockito.when(Object) because the compiler does not like void methods inside brackets... Stubbing void需要Mockito.when(Object)的不同方法,因为编译器不喜欢括号内的void方法...

Example: 例:

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

See examples in javadoc for Mockito 请参阅javadoc for Mockito中的示例

Suppose you have 假设你有

public class IFace {
    public void yourMethod() {            
    }
}

Then to mock it you need 然后嘲笑你需要的

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

If you really don't like to return null in your implementation of Answer , you can create your own Answer implementation that delegates to a void method: 如果你真的不想在你的Answer实现中return null ,你可以创建自己的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);    
}

Then your test has one line less: 然后你的测试少了一行:

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

Or, if you just need the arguments from the invocation: 或者,如果您只需要调用的参数:

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

In addition to https://stackoverflow.com/a/32139397/4471199 you can use lambda:除了https://stackoverflow.com/a/32139397/4471199之外,您还可以使用 lambda:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM