【问题标题】:Mockito: how to stub void methods to run some code when calledMockito:如何存根 void 方法在调用时运行一些代码
【发布时间】:2015-11-15 08:03:13
【问题描述】:

我想存根存储库类来测试另一个具有存储库的类(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
}

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

如果我这样做:

IRepo repoMock = mock(IRepo.class);

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

我可以创建一个Set<String> 并仅存根这两种方法以在片场上工作。然后我会实例化一个拥有 IRepo 的 Holder,注入部分存根的 mock,并在执行完 holder 后,检查集合以验证它是否包含它应该包含的内容。

我已经设法使用已弃用的方法 stubVoid 部分存根像 remove 这样的 void 方法:

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 中运行。

【问题讨论】:

    标签: java unit-testing mockito void stubbing


    【解决方案1】:

    除了https://stackoverflow.com/a/32139397/4471199,你还可以使用 lambda:

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

    【讨论】:

      【解决方案2】:

      假设你有

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

      【讨论】:

        【解决方案3】:

        你可以使用

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

        来自 Javadoc:

        当你想用泛型存根一个 void 方法时使用 doAnswer() 回答。

        存根空洞需要与 Mockito.when(Object) 不同的方法 因为编译器不喜欢括号内的 void 方法...

        例子:

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

        查看 javadoc 中 Mockito 的示例

        【讨论】:

        • 有趣的是,没有 doSomething 接受 Runnable 而不必破解无效的答案。到目前为止,我的用例是否不值得出现在 Mockito 的 API 中?
        • 我怀疑是否有任何图书馆可以满足所有用户。 Mockito 非常容易扩展(见我的回答),几乎可以用于任何目的。顺便说一句,Runnable 在您的示例中不起作用,因为您需要调用参数。
        • 修复了我在测试中途强行更改某些数据的问题,谢谢!
        【解决方案4】:

        如果您真的不喜欢在您的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);    
        }
        

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多