java Mockito:方法的返回值取决于调用的其他方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12656479/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 09:43:24  来源:igfitidea点击:

Mockito: method's return value depends on other method called

javajunitmockito

提问by pavel_kazlou

In my unit test I need to mock an interface which among different methods has nextItem()and isEmpty()methods:

在我的单元测试中,我需要模拟一个接口,该接口在不同方法之间具有nextItem()isEmpty()方法:

public interface MyQueue {
    Item nextItem();
    boolean isEmpty();
    //other methods
    ...
}

My requirement for the mock is that isEmpty()initially should return false, but after nextItem()was called isEmpty()should return true. Thus I'm mocking a queue with one item.

我对模拟的要求是isEmpty()最初应该返回 false,但在nextItem()被调用之后isEmpty()应该返回 true。因此,我正在用一项来模拟队列。

  1. What is the simplest way to implement this kind of mock with mockito?
  2. Can I implement additional requirement: calling nextItem()second, third time and so on will result in a specific kind of exception?
  1. 用 mockito 实现这种模拟的最简单方法是什么?
  2. 我可以实现额外的要求:调用nextItem()第二次、第三次等会导致特定类型的异常吗?

P.S. I don't want to provide the full implementation of my interface for the test, because of other methods in it, resulting in hard-to-understand and verbose code.

PS我不想为测试提供我的接口的完整实现,因为里面有其他方法,导致难以理解和冗长的代码。

回答by Assen Kolov

You can achieve that with thenAnswer(), a feature Mockito documentation sees as controversial:

您可以使用 thenAnswer() 实现这一点,Mockito 文档中的一项功能有争议:

Yet another controversial feature which was not included in Mockito originally. We recommend using simple stubbing with toReturn() or toThrow() only. Those two should be just enough to test/test-drive any clean & simple code.

另一个有争议的功能最初没有包含在 Mockito 中。我们建议仅使用带有 toReturn() 或 toThrow() 的简单存根。这两个应该足以测试/测试驱动任何干净和简单的代码。

Here's thenAnswer:

下面是答案:

private boolean called = false;

when(mock.nextItem()).thenAnswer(new Answer() {
 Object answer(InvocationOnMock invocation) {   
     called = true;       
     return item;
 }
when(mock.isEmpty()).thenAnswer(new Answer() {
 Object answer(InvocationOnMock invocation) {          
     return called;
 }
});

回答by Tomasz Nurkiewicz

Here is a simple example:

这是一个简单的例子:

//given
MyQueue mock = mock(MyQueue.class);

given(mock.isEmpty()).willReturn(false, true);
given(mock.nextItem()).willReturn(someItem);

//when
mock.isEmpty();   //yields false
mock.nextItem();  //yields someItem
mock.isEmpty();   //yields true

//then
InOrder inOrder = inOrder(mock);
inOrder.verify(mock).isEmpty();
inOrder.verify(mock).nextItem();
inOrder.verify(mock).isEmpty();

willReturn(false, true)means: return falseon first invocation and trueon second. InOrderobject is used to verify invocation order. Change the order or remove nextItem()call and the test will fail.

willReturn(false, true)意味着:在第一次调用和第二次调用时返回falsetrueInOrder对象用于验证调用顺序。更改顺序或删除nextItem()调用,测试将失败。

Alternatively you can use this syntax:

或者,您可以使用以下语法:

given(mock.isEmpty()).
        willReturn(false).
        willReturn(true).
        willThrow(SpecialException.class);

If you need even stronger mocking semantics, you can introduce heavy artillery - custom answer callback:

如果你需要更强大的模拟语义,你可以引入重炮-自定义应答回调:

given(mock.isEmpty()).willAnswer(new Answer<Boolean>() {
    private int counter = 0;
    @Override
    public Boolean answer(InvocationOnMock invocation) throws Throwable {
        switch(++counter) {
            case 1: return false;
            case 2: return true;
            default: throw new SpecialException();
        }
    }
});

But this can easily lead to unmaintainable test code, use with caution.

但这很容易导致无法维护的测试代码,慎用。

Finally you can spy your real objectby mocking only selected methods.

最后,您可以通过仅模拟选定的方法来监视您的真实对象

回答by Matt

You could provide some custom Answer implementations one of which depends on the other:

您可以提供一些自定义的 Answer 实现,其中一个依赖于另一个:

public class NextItemAnswer implements Answer<Item> {
   private int invocationCount = 0;
   private Item item;
   public NextItemAnswer(Item item) {
       this.item = item;
   }

   public Item answer(InvocationOnMock invocation) throws Throwable {
       invocationCount++;
       return item;
   }

   public int getInvocationCount() {
       return invocationCount;
   }
}

public class IsEmptyAnswer implements Answer<Boolean> {
   private NextItemAnswer nextItemAnswer;
   public IsEmptyAnswer(NextItemAnswer nextItemAnswer) {
       this.nextItemAnswer = nextItemAnswer;
   }
   public Boolean answer(InvocationOnMock invocation) throws Throwable {
       return nextItemAnswer.getInvocationCount() >= 0;
   }
}

and then use it:

然后使用它:

NextItemAnswer nextItemAnswer = new NextItemAnswer(item);
IsEmptyAnswer isEmptyAnswer = new IsEmptyAnswer(nextItemAnswer);

when(mock.isEmpty()).thenAnswer(isEmptyAnswer);
when(mock.nextItem()).thenAnswer(nextItemAnswer);

You might to tweak as i haven't tested this code, but the approach should be what you need.

由于我尚未测试此代码,因此您可能会进行调整,但该方法应该是您所需要的。

回答by Eric

I do realize that you explicitly wrote that you don't want to provide a full implementation of MyQueue, but, to be honest, that'd be the first thing I'd do.

我确实意识到您明确写道您不想提供 MyQueue 的完整实现,但是,老实说,这是我要做的第一件事。

In fact, I regularly provide 'mock' implementations of reasonably complex interfaces/objects for the purpose of making tests easier to test. I'm not the only one to think that: Spring Framework provides lots of mocked versions of complex objects (MockHttpServletRequest, MockHttpServletResponse, etc.), for example.

事实上,我经常提供相当复杂的接口/对象的“模拟”实现,目的是使测试更容易测试。我并不是唯一一个这么认为的人:例如,Spring Framework 提供了许多复杂对象(MockHttpServletRequest、MockHttpServletResponse 等)的模拟版本。

In that case, I'd avoid cluttering my test and provide this class either in a separate package or even in the production code.

在这种情况下,我会避免混淆我的测试并在单独的包中甚至在生产代码中提供此类。

A MockQueue would make your tests a lot more readable than the other (however correct) responses given here suggest.

MockQueue 会让你的测试比这里给出的其他(无论正确的)响应更具可读性。

回答by dvelopp

You can make a utility method and then use it wherever you want.

你可以制作一个实用方法,然后在任何你想要的地方使用它。

public static boolean mockHasInvocation(Object mock, String methodName, Object... args) {
    return mockingDetails(mock).getInvocations().stream()
            .anyMatch(o -> o.getMethod().getName().equals(methodName) && Arrays.equals(o.getArguments(), args));
}

Simple usage:

简单用法:

if(mockHasInvocation(mockObject, "methodName", "argument1", "argument2")){doSomething();}

In this case you don't need any additional variables and it's more "Mockito style".

在这种情况下,您不需要任何额外的变量,它更像是“Mockito 风格”。

回答by Don Roby

You can tell mockito to answer differently on successive calls to the same mocked method using the techniques described in the mockito docs.

您可以使用mockito 文档中描述的技术告诉 mockito 在对同一模拟方法的连续调用时做出不同的回答。

when(mock.isEmpty())
  .thenReturn(false)
  .thenReturn(true);

will make the isEmpty()call return true only on the first call, and

isEmpty()只会在第一次调用时使调用返回 true,并且

when(mock.nextItem())
  .thenReturn(item)
  .thenThrow(new NextOnEmptyQueueException())

will make nextItem()return something on the first call and throw an exception on later calls.

nextItem()在第一次调用时返回一些东西,并在以后的调用中抛出异常。

I don't know that it's possible to make the result of one of these methods depend on sequencing of calls to the other though. If it is indeed possible, I'm sure it's a great deal more complex.

我不知道有可能使这些方法之一的结果取决于对另一个方法的调用顺序。如果确实有可能,我敢肯定它要复杂得多。