如何使用mockito模拟void方法

ibr*_*maz 875 java unit-testing mocking void mockito

如何用void返回类型模拟方法?

我实现了一个观察者模式,但我无法用Mockito模拟它,因为我不知道如何.

我试图在互联网上找到一个例子,但没有成功.

我的班级看起来像

public class World {

    List<Listener> listeners;

    void addListener(Listener item) {
        listeners.add(item);
    }

    void doAction(Action goal,Object obj) {
        setState("i received");
        goal.doAction(obj);
        setState("i finished");
    }

    private string state;
    //setter getter state
} 

public class WorldTest implements Listener {

    @Test public void word{
    World  w= mock(World.class);
    w.addListener(this);
    ...
    ...

    }
}

interface Listener {
    void doAction();
}
Run Code Online (Sandbox Code Playgroud)

系统不会通过模拟触发.=(我想显示上面提到的系统状态.并根据它们做出断言.

sat*_*esh 1076

看看Mockito API文档.由于链接的文档提到(点#12),你可以使用任何的doThrow(),doAnswer(),doNothing(),doReturn()家人从框架的Mockito的方法来嘲笑无效的方法.

例如,

Mockito.doThrow(new Exception()).when(instance).methodName();
Run Code Online (Sandbox Code Playgroud)

或者如果你想将它与后续行为相结合,

Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();
Run Code Online (Sandbox Code Playgroud)

假设你正在嘲笑setState(String s)下面的类World中的setter 是代码使用doAnswer方法来模拟setState.

World  mockWorld = mock(World.class); 
doAnswer(new Answer<Void>() {
    public Void answer(InvocationOnMock invocation) {
      Object[] args = invocation.getArguments();
      System.out.println("called with arguments: " + Arrays.toString(args));
      return null;
    }
}).when(mockWorld).setState(anyString());
Run Code Online (Sandbox Code Playgroud)

  • @qualidafial:是的,我认为Void的参数化会更好,因为它更好地传达了我对返回类型不感兴趣.我不知道这个结构,谢谢你指出它. (8认同)
  • doThrow现在是#5(同样对于我来说,使用doThrow可以解决以下问题:对于追随者来说,“此处不允许输入'void'类型” ...) (2认同)
  • @DeanHiller注意到[`setRate()`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/RateLimiter.html#setRate%28double%29)是'final`,因此不能被嘲笑.而是尝试`create()` - 一个实现你需要的实例.应该没有必要模拟`RateLimiter`. (2认同)

小智 102

我想我已经找到了一个更简单的问题答案,只为一个方法调用真正的方法(即使它有一个void返回)你可以这样做:

Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();
Run Code Online (Sandbox Code Playgroud)

或者,您可以为该类的所有方法调用实际方法,执行以下操作:

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);
Run Code Online (Sandbox Code Playgroud)

  • 这是真正的答案.spy()方法工作正常,但通常是在您希望对象正常执行大部分操作时保留的. (12认同)

Omr*_*374 66

添加到@sateesh所说的,当你只想模拟一个void方法以防止测试调用它时,你可以这样使用Spy:

World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();
Run Code Online (Sandbox Code Playgroud)

如果要运行测试,请确保在spy对象上调用方法而不是world对象.例如:

assertEquals(0,spy.methodToTestThatShouldReturnZero());
Run Code Online (Sandbox Code Playgroud)


ibr*_*maz 58

所谓问题的解决方案是使用spy Mockito.spy(...)而不是mock Mockito.mock(..).

间谍使我们能够进行部分嘲弄.Mockito擅长此事.因为你的课程不完整,所以你可以通过这种方式在这个课堂上模拟一些必修课.

  • 不,这样做实际上不会有助于模拟void方法.诀窍是使用sateesh答案中列出的四种Mockito静态方法之一. (31认同)
  • 我在这里偶然发现是因为我遇到了类似的问题(巧合的是,碰巧正在测试主题/观察者的互动).我已经在使用间谍,但我希望'SubjectChanged'方法做一些不同的事情.我可以使用`verify(observer).subjectChanged(subject)来查看方法被调用.但是,出于某种原因,我宁愿覆盖这个方法.为此,Sateesh的方法和你的答案的组合是要走的路...... (2认同)
  • @Gurnard的问题请看http://stackoverflow.com/questions/1087339/using-mockito-to-test-abstract-classes. (2认同)

fl0*_*l0w 31

首先:你应该总是导入mockito static,这样代码将更具可读性(和直观性):

import static org.mockito.Mockito.*;
Run Code Online (Sandbox Code Playgroud)

对于部分嘲笑并仍然保持其余的mockito提供"Spy"的原始功能.

您可以按如下方式使用它:

private World world = spy(World.class);
Run Code Online (Sandbox Code Playgroud)

要消除执行方法,您可以使用以下内容:

doNothing().when(someObject).someMethod(anyObject());
Run Code Online (Sandbox Code Playgroud)

为方法提供一些自定义行为使用"when"与"thenReturn":

doReturn("something").when(this.world).someMethod(anyObject());
Run Code Online (Sandbox Code Playgroud)

有关更多示例,请在doc中找到mockito示例.

  • 静态导入与使其更具可读性有什么关系? (4认同)
  • 没什么。 (2认同)
  • 我认为这是一个品味问题,我只是想使语句看起来(几乎)像一个英语句子,而Class.methodname()。something()与methodname()的关系。 (2认同)

Dil*_*sha 24

如何使用mockito模拟void方法 - 有两种选择:

  1. doAnswer - 如果我们希望我们的模拟void方法做某事(尽管是无效的模拟行为).
  2. doThrow- Mockito.doThrow()如果你想从模拟的void方法中抛出一个异常,那就有了.

以下是如何使用它的示例(不是理想的用例,只是想说明基本用法).

@Test
public void testUpdate() {

    doAnswer(new Answer<Void>() {

        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            Object[] arguments = invocation.getArguments();
            if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {

                Customer customer = (Customer) arguments[0];
                String email = (String) arguments[1];
                customer.setEmail(email);

            }
            return null;
        }
    }).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("new@test.com")));

}

@Test(expected = RuntimeException.class)
public void testUpdate_throwsException() {

    doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

}
}
Run Code Online (Sandbox Code Playgroud)

您可以在我的帖子中找到有关如何使用Mockito 模拟测试 void方法的更多详细信息如何使用Mockito 进行模拟(带示例的综合指南)

  • 很好的例子。注意:在 java 8 中,使用 lambda 而不是匿名类可能会更好一些: 'doAnswer((Answer&lt;Void&gt;) invocation -&gt; { //CODE }).when(mockInstance).add(method ());' (2认同)

jav*_*y79 16

添加另一个答案(没有双关语)......

如果你不能\不想使用间谍,你需要调用doAnswer方法.但是,您不一定需要推出自己的答案.有几个默认实现.值得注意的是,CallsRealMethods.

在实践中,它看起来像这样:

doAnswer(new CallsRealMethods()).when(mock)
        .voidMethod(any(SomeParamClass.class));
Run Code Online (Sandbox Code Playgroud)

要么:

doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
        .voidMethod(any(SomeParamClass.class));
Run Code Online (Sandbox Code Playgroud)


Tim*_*m B 15

在Java 8中,假设您具有以下静态导入,则可以使其更加清晰org.mockito.Mockito.doAnswer:

doAnswer(i -> {
  // Do stuff with i.getArguments() here
  return null;
}).when(*mock*).*method*(*methodArguments*);
Run Code Online (Sandbox Code Playgroud)

return null;很重要,如果没有它,编译将失败并出现一些相当模糊的错误,因为它无法找到合适的覆盖doAnswer.

例如ExecutorService,只需立即执行任何Runnable传递,execute()就可以使用以下方法实现:

doAnswer(i -> {
  ((Runnable) i.getArguments()[0]).run();
  return null;
}).when(executor).execute(any());
Run Code Online (Sandbox Code Playgroud)

  • 在一行中:Mockito.doAnswer((i)-&gt; null).when(instance).method(any()); (2认同)

ash*_*ley 7

我认为你的问题是由于你的测试结构.我发现很难将mocking与在测试类中实现接口的传统方法混合在一起(正如你在这里所做的那样).

如果您将侦听器实现为Mock,则可以验证交互.

Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();
Run Code Online (Sandbox Code Playgroud)

这应该让你满意的是'世界'正在做正确的事情.