调用未存根化的方法时抛出RuntimeException异常

49

我正在使用Mockito。我希望在调用未存根化的方法时抛出RuntimeException

有没有什么方法可以做到这一点?


17
由于我经常忘记模拟一些方法,异常可以帮助找到这些方法。 - Hlib
3
但是如果我没有处理模拟对象的方法,那么这个方法会返回null(如果它必须返回Object)。 - Hlib
3
我认为他是指所有未存根的方法。 - Tom Verelst
请参见问题346:Answers.THROWS_EXCEPTION,以获取向Mockito添加此行为的工单。 - Joe
2
这种不是默认行为有点令人困惑! - KaptajnKold
显示剩余3条评论
4个回答

45

你可以为模拟设置一个默认答案。所有未进行存根化处理的方法将使用此默认答案。

public void testUnstubbedException() {
    // Create a mock with all methods throwing a RuntimeException by default
    SomeClass someClass = mock( SomeClass .class, new RuntimeExceptionAnswer() );

    doReturn(1).when(someClass).getId(); // Must use doReturn

    int id = someClass.getId(); // Will return 1

    someClass.unstubbedMethod(); // Will throw RuntimeException
}

public static class RuntimeExceptionAnswer implements Answer<Object> {

    public Object answer( InvocationOnMock invocation ) throws Throwable {
        throw new RuntimeException ( invocation.getMethod().getName() + " is not stubbed" );
    }

}

请注意,您不能在此功能中使用when,因为该方法在when之前被调用(Mockito when() invocation work是如何工作的?),并且它将在模拟进入存根模式之前抛出RuntimeException

因此,您必须使用doReturn才能使其正常工作。


很遗憾,这需要使用doReturn并进行一些额外的工作,比人们对于这样一个常见的需求所期望的要多。 - Charlie
我已经有一堆测试是由别人实现的。现在这些测试失败了,因为新添加的方法和类没有被模拟,但我不知道具体是哪些,因为它们只是简单地返回null。我希望设置所有未被模拟的方法抛出异常。要求我将所有的when函数重写为doReturn是一项我不想做的大量重写工作。 - undefined

15

16
如果您的测试由于调用未模拟方法而导致空指针引用而在此处之前难以理解地崩溃,则此方法无法帮助您。 - David Given
这个解决方案只在空值没有被解引用时起作用,而Guillaume Berche的解决方案只在空值被解引用时起作用。通过结合这两个解决方案,可以涵盖两种情况。 - undefined
@Zoltan,你能解释一下你的意思吗?无论是问题还是我的解决方案都没有提到null;所以我无法理解你说我的解决方案只在null不被解引用时才有效的说法。什么null? - undefined
@DawoodibnKareem:模拟对象的未存根方法默认返回null(或0或false)。David Given在上面评论中指出,将verifyNoMoreInteractions添加到测试用例的末尾无法帮助诊断意外未存根的方法,如果它们在之前引发了空指针异常。Guillaume Berche发表了一个答案,当使用“智能”空值时,提供了一个更有用的失败消息,但在未解引用(智能)空值的情况下无法帮助。在我看来,这两个答案的结合涵盖了所有情况。 - undefined
不言而喻,如果你在空指针之前解引用,那么特定的代码行将无法运行。但还是谢谢你详细解释你的评论。 - undefined

1

对Tom的回答进行改进https://dev59.com/omUo5IYBdhLWcg3wtRMx#15835255

Mockito现在提供了内置支持,具有默认抛出异常的答案,并且可以使用@Mock注释指定

    @Mock(answer = Answers.RETURNS_SMART_NULLS)
    private BackingAppDeploymentService backingAppDeploymentService;

或者

    private BackingAppDeploymentService  backingAppDeploymentService =
      Mockito.mock(BackingAppDeploymentService.class, new ReturnsSmartNulls());

查看https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#RETURNS_SMART_NULLS

如果您的代码使用未存根调用返回的对象,则会得到NullPointerException。 Answer的这个实现返回SmartNull而不是null。 SmartNull比NPE提供更好的异常信息,因为它指出了调用未存根方法的行。 您只需单击堆栈跟踪。

ReturnsSmartNulls首先尝试返回普通值(零、空集合、空字符串等),然后尝试返回SmartNull。 如果返回类型是final,则返回普通null。

ReturnsSmartNulls可能是Mockito4.0.0中默认的返回值策略


2
这不是被问到的内容。如果“智能空值”没有被使用,你将不会得到运行时异常。因此,这并没有回答关于“忘记模拟方法时是否会出现异常”的问题。 - the_dark_destructor
SmartNulls并不能解决这个问题。我想知道在我的程序运行时,是否调用了一个被模拟的对象上的方法,但是该方法并没有被提供。默认行为是返回一个空值,在某些情况下是合理的。但是我希望抛出一个异常,因为无论这个方法调用返回什么,都不是一个有效的响应,我希望得到通知,以便我可以修复它。 - undefined
只有在null被解引用时,这个解决方案才有效;而Dawood ibn Kareem的解决方案只有在null没有被解引用时才有效。通过结合这两个解决方案,可以涵盖两种情况。 - undefined

-1
你嘲笑整个类,结果所有的方法都会返回 null。
然后你可以使用 doReturn(...) 来改变这个行为。同样地,你可以使用 doThrow(...) 使得(据我回忆只有 void)方法抛出异常。
这个回答解决了你的问题吗?

4
我认为他希望确保所有方法都被模拟,例如如果某个方法没有被模拟,Mockito会抛出一个异常。 - John Snow
5
是的!但是我希望确保所有将被调用的方法都被模拟。 - Hlib
所有在模拟类中的方法都是被模拟的 - 这是定义。 - Anders R. Bystrup
太棒了!我希望如果我没有处理方法(就像这样'when(mockedObj.getName()).thanReturn("ebala")'),那么当它被调用时,该方法会抛出RuntimeException。 - Hlib
1
这被称为存根,而不是模拟 :) - Tom Verelst
4
谢谢您提供“stubbing”这个词。 - Hlib

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接