关于Java:如何使用mockito模拟void方法

How to make mock to void methods with mockito

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

我实现了一个观测者模式,但是我不能用mockito来模拟它,因为我不知道怎么做。

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

我的课看起来像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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();
}

系统不是用模拟触发的。=(我想显示上面提到的系统状态。并根据他们做出断言。


看看Mockito API文档。正如链接文档所提到的(第12点),您可以使用从mockito框架到mock void方法的任何doThrow()doAnswer()doNothing()doReturn()系列方法。

例如,

1
Mockito.doThrow(new Exception()).when(instance).methodName();

或者如果你想把它和后续行为结合起来,

1
Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();

假设您正在下面的类世界中模拟setter setState(String s),代码使用doAnswer方法模拟setState

1
2
3
4
5
6
7
8
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());


我想我找到了一个更简单的问题答案,只需为一个方法调用real方法(即使它有一个空返回),就可以这样做:

1
2
Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();

或者,可以为该类的所有方法调用real方法,这样做:

1
<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);


除了@satesh所说的,当您只想模拟一个void方法以防止测试调用它时,您可以使用spy,如下所示:

1
2
3
World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();

当您想要运行测试时,请确保您在测试中调用了spy对象上的方法,而不是world对象上的方法。例如:

1
assertEquals(0,spy.methodToTestThatShouldReturnZero());

所谓问题的解决方法是使用spymockito.spy(…)而不是mockmockito.mock(…)。

间谍使我们能够部分嘲笑。莫基托擅长这件事。因为你的课不完整,所以你在这节课上嘲弄了一些必修的地方。


首先:您应该始终导入mockito static,这样代码的可读性(和直观性)会更高:

1
import static org.mockito.Mockito.*;

对于部分模拟和仍然保持原有功能的其余Mockito提供"间谍"。

您可以按如下方式使用:

1
private World world = spy(World.class);

要消除正在执行的方法,可以使用如下方法:

1
doNothing().when(someObject).someMethod(anyObject());

要为方法提供一些自定义行为,请使用"when"和"thenreturn":

1
doReturn("something").when(this.world).someMethod(anyObject());

更多示例请在文档中找到mockito示例。


如何用mockito模拟void方法-有两个选项:

  • doAnswer-如果我们想要我们的模拟无效方法做一些事情(尽管是无效的,但模拟行为)。
  • 如果您想从模拟的void方法中抛出一个异常,那么就有一个doThrow
  • 下面是一个如何使用它的示例(不是一个理想的用例,只是想说明基本用法)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    @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("[email protected]","[email protected]");

        //some asserts
        assertThat(customer, is(notNullValue()));
        assertThat(customer.getEmail(), is(equalTo("[email protected]")));

    }

    @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("[email protected]","[email protected]");

    }
    }

    你可以在我的文章中找到更多关于如何用mockito模拟和测试void方法的细节,如何用mockito模拟(一个综合性的例子指南)


    给这群人添加另一个答案(没有双关语的意思)。

    如果您不想使用Spy,则需要调用DoAnswer方法。但是,您不必滚动自己的答案。有几个默认实现。值得注意的是,callsrealmethods。

    实际上,它看起来是这样的:

    1
    2
    doAnswer(new CallsRealMethods()).when(mock)
            .voidMethod(any(SomeParamClass.class));

    或:

    1
    2
    doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
            .voidMethod(any(SomeParamClass.class));

    在Java 8中,如果对EDOCX1具有15的静态输入,则可以使其更干净一些:

    1
    2
    3
    4
    doAnswer(i -> {
      // Do stuff with i.getArguments() here
      return null;
    }).when(*mock*).*method*(*methodArguments*);

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

    例如,刚刚执行任何传递给execute()RunnableExecutorService可以使用以下方法实现:

    1
    2
    3
    4
    doAnswer(i -> {
      ((Runnable) i.getArguments()[0]).run();
      return null;
    }).when(executor).execute(any());


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

    如果您将侦听器实现为一个模拟,那么您可以验证交互。

    1
    2
    3
    4
    Listener listener = mock(Listener.class);
    w.addListener(listener);
    world.doAction(..);
    verify(listener).doAction();

    这应该能让你确信"世界"正在做正确的事情。


    @ashley : works for me

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class AssetChangeListenerImpl extends
    AbstractAssetChangeListener implements AssetChangeListener {

      @Override
      public void onChangeEvent(final EventMessage message) throws EventHubClientException {
          execute(message);
        }
      }
    }

    public class AbstractAssetChangeListener {
      protected  void execute( final EventMessage message ) throws EventHubClientException {
        executor.execute( new PublishTask(getClient(), message) );
    } } @RunWith(MockitoJUnitRunner.class) public class AssetChangeListenerTest extends AbstractAssetChangeListenerTest {

     public void testExecute() throws EventHubClientException {
        EventMessage message = createEventMesage(EventType.CREATE);
        assetChangeListener.execute(message);
        verify(assetChangeListener, times(1)).execute(message);
    } }