在 thenThrow 中将模拟类作为参数传递时出现 UnfinishedStubingException [英] UnfinishedStubbingException when passing mocked class as an argument in thenThrow

查看:119
本文介绍了在 thenThrow 中将模拟类作为参数传递时出现 UnfinishedStubingException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以将模拟对象作为参数传递给 thenThrow() 方法吗?我有这样的事情:

Can I pass mocked object as an argument to thenThrow() method? I have something like this:

public class MyException extends Exception {
    public MyException(MockedClass mockedClass) {
        super("My message:" + mockedClass.doSth("foo"));
    }
}

public class TestedServiceTest {
    @Mock
    MockedClass mockedClass;

    @Mock
    AnotherClass anotherClass;

    @Before
    public void init() {
        when(mockedClass.doSth(anyString())).thenAnswer(new Answer<String>() {
            @Override
            public String answer(InvocationOnMock invocation) throws Throwable {
                return invocation.getArgument(0);
            }

        });
    }

    @Test
    public void notWorkingTestMethod() {
        when(anotherClass.doSomething()).thenThrow(new MyException(mockedClass));
    }

notWorkingTestMethod() 抛出 org.mockito.exceptions.misusing.UnfinishedStubingException但是,如果我在 void 方法上使用相同的技术,它就不会再抱怨了:

notWorkingTestMethod() throws org.mockito.exceptions.misusing.UnfinishedStubbingException However if I use the same technique on void method it doesn't complain anymore:

    @Test
    public void workingTestMethod() {
        doThrow(new MyException(mockedClass)).when(anotherClass).doSomethingVoid();
    }
}

是否还有其他可能的原因它不起作用?

Is there any other possible reason it doesn't work?

推荐答案

要了解为什么会发生这种情况,您需要对 Mockito 的工作原理有所了解.

To understand why this is happening you need to understand a bit about how Mockito works.

Mockito 使用内部静态状态来跟踪正在对哪些模拟进行哪些设置.这确实允许进行清晰而富有表现力的嘲讽,但有时它确实会违反最小惊讶原则,就像您在这里遇到的那样.

Mockito uses internal static state to keep track of what setup is being done to which mocks. This does allow for clear and expressive mocking, but sometimes it does cause a violation of the Principle of Least Astonishment, as it seems you've encountered here.

让我们考虑一下您不起作用的测试方法中的那一行:

Let's consider the line in your not-working test method:

when(anotherClass.doSomething()).thenThrow(new MyException(mockedClass));

Mockito 按以下顺序查看这些交互:

Mockito sees these interactions, in the following order:

  1. anotherClass.doSomething() 的调用,Mockito 将在内部将其记录为模拟的最后一次调用,因为该模拟方法可能即将设置为执行某些操作.
  2. 对静态when 方法的调用,因此Mockito 知道anotherClass.doSomething() 的行为正在被设置.
  3. MyException 构造函数中调用mockedClass.doSth().这是 Mockito 没想到的另一个模拟调用.
  1. a call to anotherClass.doSomething(), which Mockito will record internally as the last invocation on a mock, because this mock method might be about to be set up to do something.
  2. a call to the static when method, so Mockito knows that the behaviour of anotherClass.doSomething() is being set up.
  3. a call to mockedClass.doSth() in the MyException constructor. This is another invocation on a mock, which Mockito wasn't expecting.

此时,doThrow()方法还没有被调用,所以Mockito无法知道你稍后会调用它来设置异常抛出.相反,它看起来像是在写 Mockito:

At this point, the doThrow() method hasn't been called, so Mockito can't know that you will later call it to set up the exception to throw. Instead, it looks to Mockito as if you are writing:

when(anotherClass.doSomething());
when(mockedClass.doSth()).then....

因此,未完成的存根是个例外.

Hence the exception about unfinished stubbing.

正如@marcellorvalle 在评论中所建议的,修复方法是将异常移出到局部变量中:

The fix, as suggested by @marcellorvalle in the comment, is to move the exception out into a local variable:

MyException myException = new MyException(mockedClass);
when(anotherClass.doSomething()).thenThrow(myException);

在大多数情况下,像这样提取局部变量不会改变代码的行为.但它确实改变了我上面列出的与 Mockito 的三个交互的顺序.现在是:

In most cases, extracting a local variable like this won't change the behaviour of the code. But it does change the order of the three interactions with Mockito I listed above. It is now:

  1. 在异常的构造函数中调用 mockedClass.doSth(),Mockito 将在内部记录为模拟的最后一次调用.
  2. anotherClass.doSomething() 的调用,Mockito 将在内部将其记录为模拟上的最后一次调用,替换前一次调用.
  3. 对静态when 方法的调用,因此Mockito 知道anotherClass.doSomething() 的行为正在被设置.
  1. a call to mockedClass.doSth() in the constructor of your exception, which Mockito will record internally as the last invocation on a mock.
  2. a call to anotherClass.doSomething(), which Mockito will record internally as the last invocation on a mock, replacing the previous one.
  3. a call to the static when method, so Mockito knows that the behaviour of anotherClass.doSomething() is being set up.

与 Mockito 的下一个交互是对 thenThrow() 的调用,然后 Mockito 可以链接到对 anotherClass.doSomething() 的调用.

The next interaction with Mockito is then the call to thenThrow(), which Mockito can then link to the call to anotherClass.doSomething().

至于你的 workingTestMethod() 方法,它有一行

As for your workingTestMethod() method, it has the line

doThrow(new MyException(mockedClass)).when(anotherClass).doSomethingVoid();

这个模拟设置有效,因为这一次,与 Mockito 的交互顺序是:

This mock setup works, because this time, the order of interactions with Mockito is:

  1. 在异常的构造函数中调用 mockedClass.doSth(),Mockito 将在内部记录为模拟的最后一次调用.(碰巧在这种情况下,不使用最后一次调用.)
  2. 对静态doThrow() 方法的调用.此时Mockito不知道是什么mock或者什么方法抛出异常,所以只能记录异常.
  3. 调用 doThrow() 返回的 Stubber 实例上的 when 方法.这告诉 Mockito 正在设置哪个模拟,并且还要注意模拟方法的下一次调用是什么,因为这就是正在设置的内容.看起来这个 when 方法返回给定的模拟.
  4. 对模拟的 doSomethingVoid() 方法的调用.然后 Mockito 可以将要抛出的异常链接到此方法.
  1. a call to mockedClass.doSth() in the constructor of your exception, which Mockito will record internally as the last invocation on a mock. (It happens that in this case, this last-invocation isn't used.)
  2. a call to the static doThrow() method. At this point, Mockito doesn't know what mock or what method to throw the exception for, so it can only make a note of the exception.
  3. a call to the when method on the Stubber instance that doThrow() returns. This tells Mockito which mock is being set up, and also to watch out for whatever the next invocation of a mock method is, as that is what is being set up. It looks like this when method returns the mock that it is given.
  4. a call to the doSomethingVoid() method of your mock. Mockito can then link the exception that was to be thrown to this method.

这篇关于在 thenThrow 中将模拟类作为参数传递时出现 UnfinishedStubingException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆